Import the WebKit version
authorKalle Vahlman <kalle.vahlman@movial.com>
Mon, 3 Nov 2008 13:04:20 +0000 (15:04 +0200)
committerKalle Vahlman <kalle.vahlman@movial.com>
Mon, 3 Nov 2008 13:04:20 +0000 (15:04 +0200)
13 files changed:
jscorebus/build.mk [new file with mode: 0644]
jscorebus/jscorebus-classfactory.c [new file with mode: 0644]
jscorebus/jscorebus-classfactory.h [new file with mode: 0644]
jscorebus/jscorebus-marshal.c [new file with mode: 0644]
jscorebus/jscorebus-marshal.h [new file with mode: 0644]
jscorebus/jscorebus-method.c [new file with mode: 0644]
jscorebus/jscorebus-method.h [new file with mode: 0644]
jscorebus/jscorebus-signal.c [new file with mode: 0644]
jscorebus/jscorebus-signal.h [new file with mode: 0644]
jscorebus/jscorebus-signature.c [new file with mode: 0644]
jscorebus/jscorebus.c [new file with mode: 0644]
jscorebus/jscorebus.h [new file with mode: 0644]
jscorebus/jscorebus.pc [new file with mode: 0644]

diff --git a/jscorebus/build.mk b/jscorebus/build.mk
new file mode 100644 (file)
index 0000000..6edbdde
--- /dev/null
@@ -0,0 +1,20 @@
+NAME := jscorebus
+VERSION := 0.0.1
+SOURCES := $(wildcard jscorebus/*.c)
+CFLAGS := -ggdb -DDEBUG
+PKGS := gobject-2.0 dbus-1 dbus-glib-1 webkit-1.0
+
+INCDIR := $(DESTDIR)/$(PREFIX)/include/$(NAME)
+PCDIR := $(DESTDIR)/$(PREFIX)/lib/pkgconfig
+
+include build/library.mk
+
+install::
+ifneq ($(wildcard $(O_LIBRARY)),)
+       install -d $(INCDIR) $(PCDIR)
+       install -m 0644 jscorebus/jscorebus.h $(INCDIR)
+       install -m 0644 jscorebus/jscorebus.pc $(PCDIR)
+       sed -i s,@PREFIX@,$(PREFIX), $(PCDIR)/jscorebus.pc
+else
+       @echo JSCoreBus has not been built, not installing
+endif
diff --git a/jscorebus/jscorebus-classfactory.c b/jscorebus/jscorebus-classfactory.c
new file mode 100644 (file)
index 0000000..812eca8
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <JavaScriptCore/JavaScript.h>
+#include <glib.h>
+
+#include "jscorebus-classfactory.h"
+
+static
+GHashTable *definitions = NULL;
+static
+GHashTable *classes = NULL;
+
+void
+jsclassdef_insert(const char *class_name, const JSClassDefinition *definition)
+{
+  if (G_UNLIKELY(definitions == NULL))
+  {
+    definitions = g_hash_table_new(g_str_hash, g_str_equal);
+  }
+
+  g_hash_table_insert(definitions, (gpointer)class_name, (gpointer)definition);
+}
+
+const JSClassDefinition *
+jsclassdef_lookup(const char *class_name)
+{
+  if (class_name == NULL)
+  {
+    return NULL;
+  }
+  
+  if (G_UNLIKELY(definitions == NULL))
+  {
+    return NULL;
+  }
+  
+  return (const JSClassDefinition *) g_hash_table_lookup(definitions,
+                                                         (gpointer)class_name);
+}
+
+JSClassRef
+jsclass_lookup(const JSClassDefinition *definition)
+{
+  JSClassRef jsclass;
+  
+  if (G_UNLIKELY(classes == NULL))
+  {
+    classes = g_hash_table_new(NULL, NULL);
+  }
+  
+  jsclass = g_hash_table_lookup(classes, (gpointer)definition);
+  if (G_UNLIKELY(jsclass == NULL))
+  {
+    jsclass = JSClassCreate(definition);
+    g_hash_table_insert(classes, (gpointer)definition, (gpointer)jsclass);
+  }
+  
+  return jsclass;
+}
+
diff --git a/jscorebus/jscorebus-classfactory.h b/jscorebus/jscorebus-classfactory.h
new file mode 100644 (file)
index 0000000..e36aa95
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __JSCOREBUS_CLASSFACTORY_H___
+#define __JSCOREBUS_CLASSFACTORY_H___
+
+G_BEGIN_DECLS
+
+void
+jsclassdef_insert(const char *class_name, const JSClassDefinition *definition);
+
+const JSClassDefinition *
+jsclassdef_lookup(const char *class_name);
+
+JSClassRef
+jsclass_lookup(const JSClassDefinition *definition);
+
+G_END_DECLS
+
+#endif /* __JSCOREBUS_CLASSFACTORY_H___ */
+
diff --git a/jscorebus/jscorebus-marshal.c b/jscorebus/jscorebus-marshal.c
new file mode 100644 (file)
index 0000000..b2c3600
--- /dev/null
@@ -0,0 +1,673 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include "jscorebus-classfactory.h"
+#include "jscorebus-marshal.h"
+
+static gboolean
+jsvalue_append_basic(JSContextRef context,
+                     JSValueRef jsvalue,
+                     int type,
+                     DBusMessageIter *iter);
+
+
+gboolean jsvalue_array_append_to_message_iter (JSContextRef context,
+                                               const JSValueRef jsvalues[],
+                                               int n_values,
+                                               DBusMessageIter *iter,
+                                               const char *signature)
+{
+  DBusSignatureIter siter;
+  DBusError error;
+  char *sig = (char*) signature; /* This is a bit silly, I know */
+  int i = 0;
+
+  /* If there is no signature, we need to do some autodetection */
+  if (signature == NULL)
+  {
+    char **parts;
+    parts = g_new0(char*, n_values + 1);
+    for (i = 0; i < n_values; i++)
+    {
+      parts[i] = jsvalue_to_signature(context, jsvalues[i]);
+    }
+    sig = g_strjoinv(NULL, parts);
+    g_strfreev(parts);
+  }
+
+  /* If there *still* is no signature, or it is empty, we bork.
+   * Empty messages have no business going through this code path.
+   */
+  if (sig == NULL || strlen(sig) == 0)
+  {
+    g_warning("Could not autodetect signature for message arguments!");
+    g_free(sig);
+    return FALSE;
+  }
+
+  /* First of all, we need to validate the signature */
+  dbus_error_init(&error);
+  if (!dbus_signature_validate(sig, &error))
+  {
+    g_warning(error.message);
+    if (sig != signature)
+      g_free(sig);
+    return FALSE;
+  }
+  
+  dbus_signature_iter_init(&siter, sig);
+  i = 0;
+  do {
+    const char *arg_sig = dbus_signature_iter_get_signature(&siter);
+    if (!jsvalue_append_to_message_iter(context, jsvalues[i++], iter, arg_sig))
+    {
+      g_warning("Appending '%s' to message failed!", arg_sig);
+      if (sig != signature)
+        g_free(sig);
+      return FALSE;
+    }
+  } while (dbus_signature_iter_next(&siter));
+  
+  if (sig != signature)
+    g_free(sig);
+
+  return TRUE;
+}
+
+gboolean jsvalue_append_to_message_iter(JSContextRef context,
+                                        JSValueRef jsvalue,
+                                        DBusMessageIter *iter,
+                                        const char *signature)
+{
+  DBusSignatureIter siter;
+
+  dbus_signature_iter_init(&siter, signature);
+
+  switch (dbus_signature_iter_get_current_type(&siter))
+  {
+    /* JSValueToBoolean follows the JS rules of what's true and false so we can
+     * simply take the value without checking the type of it
+     */ 
+    case DBUS_TYPE_BOOLEAN:
+      {
+        dbus_bool_t value = JSValueToBoolean(context, jsvalue);
+        if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value))
+        {
+          g_warning("Could not append a boolean to message iter");
+          return FALSE;
+        }
+        break;
+      }
+    /* Basic types */
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT16:
+    case DBUS_TYPE_UINT32:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_BYTE:
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+      {
+        int type = dbus_signature_iter_get_current_type(&siter);
+        if (!jsvalue_append_basic(context, jsvalue, type, iter))
+        {
+          g_warning("Could not append a '%c' to message iter", type);
+          return FALSE;
+        }
+        break;
+      }
+    case DBUS_TYPE_DOUBLE:
+      {
+        /* Conversions between dbus_uint64_t and double seem to loose precision,
+         * that's why doubles are special-cased
+         */
+        double value = JSValueToNumber(context, jsvalue, NULL);
+        if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value))
+        {
+          g_warning("Could not append a double to message iter");
+          return FALSE;
+        }
+        break;
+      }
+    case DBUS_TYPE_ARRAY:
+      {
+        /* Dicts are implemented as arrays of entries in D-Bus */
+        if (dbus_signature_iter_get_element_type(&siter) == DBUS_TYPE_DICT_ENTRY)
+        {
+          int i, props;
+          JSPropertyNameArrayRef propnames;
+          char *dict_signature;
+          DBusMessageIter subiter;
+          DBusSignatureIter dictsiter;
+          DBusSignatureIter dictsubsiter;
+
+          dbus_signature_iter_recurse(&siter, &dictsiter);
+          dict_signature = dbus_signature_iter_get_signature(&dictsiter);
+
+          if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                                dict_signature, &subiter))
+          {
+            g_warning("Memory exhausted!");
+            return FALSE;
+          }
+          propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)jsvalue);
+          props = JSPropertyNameArrayGetCount(propnames);
+
+          /* Move the signature iter to the value part of the signature
+           * We only support string->value dicts currently, though we coud do
+           * numbers too...
+           */
+          dbus_signature_iter_recurse(&dictsiter, &dictsubsiter); /* Now at key */
+          dbus_signature_iter_next(&dictsubsiter); /* Now at value */
+
+          for (i = 0; i < props; i++)
+          {
+            JSStringRef jsstr;
+            DBusMessageIter dictiter;
+            char *cstr;
+            if (!dbus_message_iter_open_container(&subiter, DBUS_TYPE_DICT_ENTRY,
+                                                  NULL, &dictiter))
+            {
+              g_warning("Memory exhausted!");
+              return FALSE;
+            }
+            jsstr = JSPropertyNameArrayGetNameAtIndex(propnames, i);
+            cstr = string_from_jsstring(context, jsstr);
+            dbus_message_iter_append_basic(&dictiter, DBUS_TYPE_STRING, &cstr);
+            g_free(cstr);
+            jsvalue_append_to_message_iter(context,
+              JSObjectGetProperty(context, (JSObjectRef)jsvalue, jsstr, NULL),
+              &dictiter, dbus_signature_iter_get_signature(&dictsubsiter));
+            dbus_message_iter_close_container(&subiter, &dictiter);
+          }
+          dbus_message_iter_close_container(iter, &subiter);
+          break;
+        } else {
+          int i, props;
+          JSPropertyNameArrayRef propnames;
+          JSValueRef *jsvalues;
+          DBusMessageIter subiter;
+          DBusSignatureIter arraysiter;
+          char *array_signature = NULL;
+          if (!jsvalue_instanceof(context, jsvalue, "Array"))
+          {
+            g_warning("Expected JavaScript Array type, got %i",
+                      JSValueGetType(context, jsvalue));
+            return FALSE;
+          }
+          
+          dbus_signature_iter_recurse(&siter, &arraysiter);
+          array_signature = dbus_signature_iter_get_signature(&arraysiter);
+          
+          propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)jsvalue);
+          if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                                array_signature, &subiter))
+          {
+            g_warning("Memory exhausted!");
+            JSPropertyNameArrayRelease(propnames);
+            g_free(array_signature);
+            return FALSE;
+          }
+
+          props = JSPropertyNameArrayGetCount(propnames);
+
+          for (i = 0; i < props; i++)
+          {
+            JSValueRef value = JSObjectGetPropertyAtIndex(context,
+                                                          (JSObjectRef)jsvalue,
+                                                          i,
+                                                          NULL);
+            jsvalue_append_to_message_iter(context, value,
+                                           &subiter, array_signature);
+          }
+          dbus_message_iter_close_container(iter, &subiter);
+          g_free(array_signature);
+          JSPropertyNameArrayRelease(propnames);
+          break;
+        }
+      }
+    case DBUS_TYPE_VARIANT:
+      {
+        DBusMessageIter subiter;
+        DBusSignatureIter vsiter;
+        char *vsignature;
+        JSValueRef value = NULL;
+
+        if (jsvalue_typeof(context, jsvalue, "DBusVariant"))
+        {
+          value = (JSValueRef)JSObjectGetPrivate((JSObjectRef)jsvalue);
+        } else {
+          value = jsvalue;
+        }
+        
+        dbus_signature_iter_recurse(&siter, &vsiter);
+        vsignature = jsvalue_to_signature(context, value);
+        
+        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                              vsignature, &subiter))
+        {
+          g_warning("Memory exhausted!");
+          g_free(vsignature);
+          return FALSE;
+        }
+
+        if (!jsvalue_append_to_message_iter(context, value,
+                                            &subiter, vsignature))
+        {
+          g_warning("Failed to append variant contents with signature %s",
+                    vsignature);
+          g_free(vsignature);
+          return FALSE;
+        }
+        
+        g_free(vsignature);
+        dbus_message_iter_close_container(iter, &subiter);
+        break;
+      }
+    case DBUS_TYPE_STRUCT:
+      {
+        int i, props;
+        JSPropertyNameArrayRef propnames;
+        DBusMessageIter subiter;
+        DBusSignatureIter stsiter;
+        char *stsignature;
+        JSValueRef value = NULL;
+
+        if (jsvalue_typeof(context, jsvalue, "DBusStruct"))
+        {
+          value = (JSValueRef)JSObjectGetPrivate((JSObjectRef)jsvalue);
+        } else {
+          value = jsvalue;
+        }
+
+        propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)value);
+        props = JSPropertyNameArrayGetCount(propnames);
+
+        if (props == 0)
+        {
+          g_warning("Empty struct not allowed");
+          return FALSE;
+        }
+        
+        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+                                              NULL, &subiter))
+        {
+          g_warning("Memory exhausted!");
+          return FALSE;
+        }
+
+        dbus_signature_iter_recurse(&siter, &stsiter);
+
+        for (i = 0; i < props; i++)
+        {
+          const char *sig = dbus_signature_iter_get_signature(&stsiter);
+          JSValueRef child_value = JSObjectGetProperty(context,
+            (JSObjectRef)value,
+            JSPropertyNameArrayGetNameAtIndex(propnames, i),
+            NULL);
+
+          if (!jsvalue_append_to_message_iter(context, child_value,
+                                              &subiter, sig))
+          {
+            g_warning("Failed to append struct contents with signature %s",
+                      sig);
+            return FALSE;
+          }
+
+          if (!dbus_signature_iter_next(&stsiter))
+          {
+            break;
+          }
+        }
+        JSPropertyNameArrayRelease(propnames);
+        dbus_message_iter_close_container(iter, &subiter);
+        break;
+      }
+    default:
+      g_warning("Tried to append invalid or unsupported argument '%s' "
+                "(base type '%c') to a message", signature,
+                dbus_signature_iter_get_current_type(&siter));
+      break;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+jsvalue_append_basic(JSContextRef context,
+                     JSValueRef jsvalue,
+                     int type,
+                     DBusMessageIter *iter)
+{
+  dbus_uint64_t v = 0;
+  dbus_uint64_t *value = NULL;
+  char *strvalue = NULL;
+  switch (JSValueGetType(context, jsvalue))
+  {
+    case kJSTypeNumber:
+      {
+        v = (dbus_uint64_t)JSValueToNumber(context, jsvalue, NULL);
+        value = &v;
+        break;
+      }
+    case kJSTypeString:
+      {
+        strvalue = string_from_jsvalue(context, jsvalue);
+        break;
+      }
+    case kJSTypeObject:
+      {
+        int i;
+        for (i = 0; i < JSCOREBUS_N_NUMBER_CLASSES; i++)
+        {
+          if (jscorebus_number_class_types[i] == type
+           && jsvalue_typeof(context, jsvalue, jscorebus_number_class_names[i]))
+          {
+            value = (dbus_uint64_t *)JSObjectGetPrivate((JSObjectRef)jsvalue);
+            break;
+          }
+        }
+
+        if (jsvalue_typeof(context, jsvalue, "DBusObjectPath"))
+        {
+          strvalue = string_from_jsvalue(context,
+                                         JSObjectGetPrivate((JSObjectRef)jsvalue));
+          break;
+        }
+
+        if (jsvalue_typeof(context, jsvalue, "DBusSignature"))
+        {
+          strvalue = string_from_jsvalue(context,
+                                         JSObjectGetPrivate((JSObjectRef)jsvalue));
+          break;
+        }
+
+        /* Intentionally falls through to default */
+      }
+    case kJSTypeUndefined:
+    case kJSTypeNull:
+      {
+        g_warning("Tried to pass undefined or null as basic type");
+        break;
+      }
+    default:
+      g_warning("JSValue wasn't a '%c' (it was %i), or it is not supported",
+                type, JSValueGetType(context, jsvalue));
+      break;
+  }
+  
+  if (value != NULL && dbus_message_iter_append_basic(iter, type, value))
+  {
+    return TRUE;
+  } else if (strvalue != NULL
+          && dbus_message_iter_append_basic(iter, type, &strvalue)) {
+    g_free(strvalue);
+    return TRUE;
+  }
+  
+  g_free(strvalue);
+  return FALSE;
+}
+
+#define NUMERIC_VALUE(NAME) \
+  DBUS_TYPE_## NAME: \
+    { \
+      dbus_uint64_t value = 0; \
+      dbus_message_iter_get_basic(iter, &value); \
+      jsvalue = JSValueMakeNumber(context, (double)value); \
+      break; \
+    }
+
+JSValueRef jsvalue_from_message_iter(JSContextRef context,
+                                     DBusMessageIter *iter)
+{
+  JSValueRef jsvalue;
+  
+  switch (dbus_message_iter_get_arg_type (iter))
+  {
+    case DBUS_TYPE_BOOLEAN:
+      {
+        gboolean value;
+        dbus_message_iter_get_basic(iter, &value);
+        jsvalue = JSValueMakeBoolean(context, value);
+        break;
+      }
+    case NUMERIC_VALUE(BYTE)
+    case NUMERIC_VALUE(INT16)
+    case NUMERIC_VALUE(UINT16)
+    case NUMERIC_VALUE(INT32)
+    case NUMERIC_VALUE(UINT32)
+    case NUMERIC_VALUE(INT64)
+    case NUMERIC_VALUE(UINT64)
+    case DBUS_TYPE_DOUBLE:
+    {
+      double value = 0;
+      dbus_message_iter_get_basic(iter, &value);
+      jsvalue = JSValueMakeNumber(context, value);
+      break;
+    }
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+    case DBUS_TYPE_STRING:
+      {
+        const char *value;
+        JSStringRef jsstr;
+        dbus_message_iter_get_basic(iter, &value);
+        jsstr = JSStringCreateWithUTF8CString(value);
+        jsvalue = JSValueMakeString(context, jsstr);
+        JSStringRelease(jsstr);
+        break;
+      }
+    case DBUS_TYPE_ARRAY:
+      {
+        JSStringRef arrayProperty;
+        JSObjectRef arrayConstructor;
+        DBusMessageIter child_iter;
+        int i;
+        
+        arrayProperty = JSStringCreateWithUTF8CString("Array");
+        arrayConstructor = JSValueToObject(context,
+                            JSObjectGetProperty(context,
+                              JSContextGetGlobalObject(context),
+                              arrayProperty, NULL),
+                            NULL);
+        JSStringRelease(arrayProperty);
+
+        jsvalue = JSObjectCallAsConstructor(context, arrayConstructor,
+                                            0, NULL, NULL);
+
+        dbus_message_iter_recurse(iter, &child_iter);
+        
+        i = 0;
+        do
+        {
+          if (dbus_message_iter_get_arg_type(&child_iter) == DBUS_TYPE_DICT_ENTRY)
+          {
+            JSValueRef key;
+            JSStringRef key_str;
+            JSValueRef value;
+            DBusMessageIter dictiter;
+            
+            dbus_message_iter_recurse(&child_iter, &dictiter);
+            key = jsvalue_from_message_iter(context, &dictiter);
+            key_str = JSValueToStringCopy(context, key, NULL);
+            dbus_message_iter_next(&dictiter);
+            value = jsvalue_from_message_iter(context, &dictiter);
+
+            JSObjectSetProperty(context, (JSObjectRef)jsvalue,
+                                key_str, value, 0, NULL);
+            JSStringRelease(key_str);
+          } else {
+            JSObjectSetPropertyAtIndex(context, (JSObjectRef)jsvalue, i++, 
+              jsvalue_from_message_iter(context, &child_iter), NULL);
+          }
+        } while (dbus_message_iter_next(&child_iter));
+        
+        break;
+      }
+    case DBUS_TYPE_VARIANT:
+      {
+        DBusMessageIter child_iter;
+        dbus_message_iter_recurse(iter, &child_iter);
+        jsvalue = jsvalue_from_message_iter(context, &child_iter);
+        break;
+      }
+    case DBUS_TYPE_INVALID:
+        /* Convert invalid to undefined */
+        jsvalue = JSValueMakeUndefined(context);
+        break;
+    case DBUS_TYPE_DICT_ENTRY:
+        /* Dict entries should always be handled in the array branch */
+        g_assert_not_reached();
+    default:
+      g_warning("Could not convert value from type %c (%i)",
+                dbus_message_iter_get_arg_type (iter),
+                dbus_message_iter_get_arg_type (iter));
+      jsvalue = JSValueMakeUndefined(context);
+      break;
+  }
+
+  return jsvalue;
+}
+
+
+char *string_from_jsstring(JSContextRef context, JSStringRef jsstr)
+{
+  size_t len;
+  char *cstr;
+
+  len = JSStringGetMaximumUTF8CStringSize(jsstr);
+  cstr = g_new(char, len);
+  JSStringGetUTF8CString(jsstr, cstr, len);
+
+  return cstr;
+}
+
+char *string_from_jsvalue(JSContextRef context, JSValueRef jsvalue)
+{
+  JSStringRef jsstr;
+  char *cstr;
+
+  if (!JSValueIsString(context, jsvalue))
+  {
+    return NULL;
+  }
+
+  jsstr = JSValueToStringCopy(context, jsvalue, NULL);
+  cstr = string_from_jsstring(context, jsstr);
+  JSStringRelease(jsstr);
+  
+  return cstr;
+}
+
+JSObjectRef function_from_jsvalue(JSContextRef context,
+                                  JSValueRef value,
+                                  JSValueRef* exception)
+{
+  JSObjectRef function;
+
+  if (JSValueIsNull(context, value))
+  {
+    return NULL;
+  }
+
+  if (!JSValueIsObject(context, value) )
+  {
+    /* TODO: set exception */
+    g_warning("%s: Value wasn't an object", G_STRFUNC);
+    return NULL;
+  }
+
+  function = JSValueToObject(context, value, exception);
+  if (!JSObjectIsFunction(context, function))
+  {
+    /* TODO: set exception */
+    g_warning("%s: Value wasn't a function", G_STRFUNC);
+    return NULL;
+  }
+
+  return function;
+}
+
+void call_function_with_message_args(JSContextRef context,
+                                     JSObjectRef thisObject,
+                                     JSObjectRef function,
+                                     DBusMessage *message)
+{
+  size_t argumentCount;
+  JSValueRef *args;
+  int arg_type;
+  DBusMessageIter iter;
+
+  /**
+   * Iterate over the message arguments and append them to args
+   */
+  dbus_message_iter_init (message, &iter);
+  argumentCount = 0;
+  args = NULL;
+
+  /* Error messages should have the error name as the first param */
+  if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR)
+  {
+    const char *error_name = dbus_message_get_error_name(message);
+    
+    if (error_name != NULL)
+    {
+      JSValueRef *tmp;
+      JSStringRef jsstr;
+      
+      jsstr = JSStringCreateWithUTF8CString(error_name);
+      argumentCount++;
+      tmp = g_renew(JSValueRef, args, argumentCount);
+      args = tmp;
+      args[argumentCount-1] = JSValueMakeString(context, jsstr);;
+      JSStringRelease(jsstr);
+     }
+  }
+
+  while ((arg_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
+  {
+    JSValueRef *tmp;
+    
+    argumentCount++;
+    tmp = g_renew(JSValueRef, args, argumentCount);
+    args = tmp;
+    args[argumentCount-1] = (JSValueRef)jsvalue_from_message_iter(context, &iter);
+    if (args[argumentCount-1] == NULL) {
+      g_warning("Couldn't get argument from argument type %c", arg_type);
+      args[argumentCount-1] = JSValueMakeUndefined(context);
+    }
+    dbus_message_iter_next (&iter);
+  }
+
+  JSObjectCallAsFunction(context, function, thisObject,
+                         argumentCount, args, NULL);
+}
+
diff --git a/jscorebus/jscorebus-marshal.h b/jscorebus/jscorebus-marshal.h
new file mode 100644 (file)
index 0000000..51622fc
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Functions to convert between D-Bus types and JSCore types */
+
+#ifndef __JSCOREBUS_CONVERT_H___
+#define __JSCOREBUS_CONVERT_H___
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+/* The number class names are in guestimated usage frequency order to speed up
+ * iteration over them.
+ */
+#define JSCOREBUS_N_NUMBER_CLASSES 8
+char *jscorebus_number_class_names[JSCOREBUS_N_NUMBER_CLASSES];
+int jscorebus_number_class_types[JSCOREBUS_N_NUMBER_CLASSES];
+
+/* Append JSValues to a D-Bus message iter */
+gboolean jsvalue_array_append_to_message_iter(JSContextRef context,
+                                              const JSValueRef jsvalues[],
+                                              int n_values,
+                                              DBusMessageIter *iter,
+                                              const char *signature);
+gboolean jsvalue_append_to_message_iter(JSContextRef context,
+                                        const JSValueRef jsvalue,
+                                        DBusMessageIter *iter,
+                                        const char *signature);
+
+/* Call a JS function with the arguments from a message */
+void call_function_with_message_args(JSContextRef context,
+                                     JSObjectRef thisObject,
+                                     JSObjectRef function,
+                                     DBusMessage *message);
+
+/* To JavaScript types */
+JSValueRef jsvalue_from_message_iter(JSContextRef context,
+                                     DBusMessageIter *iter);
+JSObjectRef function_from_jsvalue(JSContextRef context,
+                                  JSValueRef value,
+                                  JSValueRef* exception);
+
+/* To D-Bus types */
+char *string_from_jsstring(JSContextRef context, JSStringRef jsstring);
+char *string_from_jsvalue(JSContextRef context, JSValueRef jsvalue);
+dbus_uint64_t jsvalue_to_number_value (JSContextRef context,
+                                       JSValueRef jsvalue,
+                                       int *number_type);
+
+/* JSValue to D-Bus signature (autodetection) */
+char *jsvalue_to_signature(JSContextRef context, JSValueRef jsvalue);
+gboolean
+jsarray_get_signature(JSContextRef context,
+                      JSValueRef jsvalue,
+                      JSPropertyNameArrayRef propNames,
+                      char **signature);
+gboolean
+jsdict_get_signature(JSContextRef context,
+                     JSValueRef jsvalue,
+                     JSPropertyNameArrayRef propNames,
+                     char **signature);
+
+
+/* Helper functions */
+gboolean jsvalue_typeof(JSContextRef context,
+                        JSValueRef jsvalue,
+                        const char *type);
+gboolean jsvalue_instanceof(JSContextRef context,
+                            JSValueRef jsvalue,
+                            const char *constructor);
+
+
+
+#endif /* __JSCOREBUS_CONVERT_H___ */
+
diff --git a/jscorebus/jscorebus-method.c b/jscorebus/jscorebus-method.c
new file mode 100644 (file)
index 0000000..892ad88
--- /dev/null
@@ -0,0 +1,452 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include "jscorebus-marshal.h"
+#include "jscorebus-method.h"
+
+/* Private data for methods */
+typedef struct _MethodPrivate
+{
+  char *destination;
+  char *object_path;
+  char *method_name;
+  char *interface;
+  char *signature;
+
+  DBusConnection *connection;
+  DBusMessage *message;
+  DBusPendingCall *pending_reply;
+
+  JSGlobalContextRef context;
+  JSObjectRef this;
+  JSObjectRef onreply;
+  JSObjectRef onerror;
+  gboolean async;
+} MethodPrivate;
+
+/* Utility functions */
+static
+JSClassRef get_class ();
+
+static
+JSValueRef call_sync(JSContextRef context, MethodPrivate *priv);
+static
+JSValueRef call_async(JSContextRef context, MethodPrivate *priv);
+static
+void call_onreply(JSContextRef context,
+                  MethodPrivate *priv,
+                  DBusMessage *message);
+static
+void call_onerror(JSContextRef context,
+                  MethodPrivate *priv,
+                  DBusMessage *message);
+
+/* JSCore methods */
+static
+void method_finalize(JSObjectRef object);
+
+static
+bool method_set_property(JSContextRef context,
+                         JSObjectRef object,
+                         JSStringRef propertyName,
+                         JSValueRef value,
+                         JSValueRef* exception);
+
+static
+JSValueRef method_call (JSContextRef context,
+                        JSObjectRef function,
+                        JSObjectRef thisObject,
+                        size_t argumentCount,
+                        const JSValueRef arguments[],
+                        JSValueRef *exception);
+
+/* The Method Class */
+static const
+JSClassDefinition method_jsclass_def =
+{
+  0,
+  kJSClassAttributeNone,
+  "DBusMethod",
+  NULL,
+
+  NULL,
+  NULL,
+  
+  NULL, /* Initialize */
+  method_finalize,
+  
+  NULL,
+  NULL,
+  method_set_property, /* SetProperty */
+  NULL,
+  NULL,
+  
+  method_call, /* CallAsFunction */
+  NULL, /* Constructor */
+  NULL,
+  NULL
+};
+
+/*** JSCore methods */
+
+static
+void method_finalize(JSObjectRef object)
+{
+  MethodPrivate *priv = (MethodPrivate *)JSObjectGetPrivate(object);
+
+  if (priv != NULL)
+  {
+
+    if (priv->pending_reply != NULL)
+    {
+      dbus_pending_call_cancel(priv->pending_reply);
+    }
+  
+    g_free(priv->destination);
+    g_free(priv->object_path);
+    g_free(priv->method_name);
+    g_free(priv->interface);
+    g_free(priv->signature);
+    
+    dbus_connection_unref(priv->connection);
+
+    priv->this = NULL;
+    priv->onreply = NULL;
+    priv->onerror = NULL;
+    priv->context = NULL;
+    
+    g_free(priv);
+    priv = NULL;
+  }
+}
+
+static
+bool method_set_property(JSContextRef context,
+                         JSObjectRef object,
+                         JSStringRef propertyName,
+                         JSValueRef value,
+                         JSValueRef* exception)
+{
+  MethodPrivate *priv;
+
+  priv = (MethodPrivate *)JSObjectGetPrivate(object);
+  g_assert(priv != NULL);
+
+  if (JSStringIsEqualToUTF8CString(propertyName, "async"))
+  {
+    if (JSValueIsBoolean(context, value))
+    { 
+      priv->async = JSValueToBoolean(context, value);
+      return TRUE;
+    }
+    
+    /* TODO: set exception */
+    g_warning("Tried to set a non-boolean to 'async'");
+    return TRUE;
+  } else if (JSStringIsEqualToUTF8CString(propertyName, "onreply")) {
+    priv->onreply = function_from_jsvalue(context, value, exception);
+    return TRUE;
+  } else if (JSStringIsEqualToUTF8CString(propertyName, "onerror")) {
+    priv->onerror = function_from_jsvalue(context, value, exception);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static
+JSValueRef method_call (JSContextRef context,
+                        JSObjectRef function,
+                        JSObjectRef thisObject,
+                        size_t argumentCount,
+                        const JSValueRef arguments[],
+                        JSValueRef *exception)
+{
+  int i;
+  MethodPrivate *priv;
+  DBusMessageIter iter;
+  JSValueRef ret;
+       
+  priv = (MethodPrivate *)JSObjectGetPrivate(function);
+  g_assert(priv != NULL);
+
+  priv->message = dbus_message_new_method_call(priv->destination,
+                                               priv->object_path,
+                                               priv->interface,
+                                               priv->method_name);
+  
+  if (argumentCount > 0)
+  {
+    /* Push arguments to the message */
+    dbus_message_iter_init_append (priv->message, &iter);
+    if (!jsvalue_array_append_to_message_iter(context,
+                                              arguments, argumentCount,
+                                              &iter, priv->signature))
+    {
+      dbus_message_unref(priv->message);
+      priv->message = NULL;
+    }
+  }
+  
+  if (priv->async)
+  {
+    ret = call_async(context, priv);
+  } else {
+    ret = call_sync(context, priv);
+  }
+
+  if (priv->message != NULL)
+  {
+    dbus_message_unref(priv->message);
+  }
+  priv->message = NULL;
+
+  return ret;
+}
+
+/*** Utility functions */
+
+static
+JSClassRef get_class ()
+{
+  JSClassRef jsclass = (JSClassRef)jsclass_lookup(&method_jsclass_def);
+  
+  if (G_LIKELY(jsclass != NULL))
+  {
+    return jsclass;
+  }
+  
+  jsclassdef_insert("DBusMethod", &method_jsclass_def);
+  jsclass = (JSClassRef)jsclass_lookup(&method_jsclass_def);
+
+  g_assert(jsclass != NULL);
+
+  return jsclass;
+}
+
+static
+JSValueRef call_sync(JSContextRef context, MethodPrivate *priv)
+{
+  /**
+   * Set up reply callback from the method.onReply property if available and
+   * send the message
+   */
+
+  if (priv->message == NULL)
+  {
+    call_onerror(context, priv, NULL);
+    return JSValueMakeUndefined(context);
+  }
+
+  if (priv->onreply != NULL)
+  {
+    DBusMessage *reply_message;
+    
+    reply_message = dbus_connection_send_with_reply_and_block(
+                      priv->connection,
+                      priv->message, -1, NULL);
+    if (reply_message != NULL)
+    {
+      if (dbus_message_get_type(reply_message) == DBUS_MESSAGE_TYPE_ERROR)
+      {
+        call_onerror(context, priv, reply_message);
+      } else if (dbus_message_get_type(reply_message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+        call_onreply(context, priv, reply_message);
+      } else {
+        g_warning("Unknown reply!");
+      }
+    } else {
+      // TODO: set exception
+      g_warning("Failed to send message to %s", priv->destination);
+      call_onerror(context, priv, NULL);
+    }
+  } else {
+    if (!dbus_connection_send(priv->connection, priv->message, NULL))
+    {
+      // TODO: set exception
+      g_warning("Failed to send message to %s", priv->destination);
+      call_onerror(context, priv, NULL);
+    }
+  }  
+
+  return JSValueMakeUndefined(context);
+}
+
+static
+void pending_call_notify(DBusPendingCall *pending,
+                         void *user_data)
+{
+  DBusMessage *reply_message;
+  MethodPrivate *priv;
+  
+  g_assert(user_data != NULL);
+  
+  priv = (MethodPrivate *)user_data;
+  g_assert(priv->pending_reply != NULL);
+
+  if (pending == NULL)
+  {
+    /* Disconnected!
+     * TODO: How do we handle these?
+     */
+    g_warning("Disconnected from the bus!");
+    priv->pending_reply = NULL;
+    return;
+  }
+  priv->pending_reply = NULL;
+  reply_message = dbus_pending_call_steal_reply(pending);
+
+  if (dbus_message_get_type(reply_message) == DBUS_MESSAGE_TYPE_ERROR)
+  {
+    call_onerror(priv->context, priv, reply_message);
+  } else if (dbus_message_get_type(reply_message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+    call_onreply(priv->context, priv, reply_message);
+  } else {
+    g_warning("Unknown reply!");
+  }
+
+}
+
+static
+JSValueRef call_async(JSContextRef context, MethodPrivate *priv)
+{
+       dbus_uint32_t serial = 0;
+
+  if (priv->message == NULL)
+  {
+    call_onerror(context, priv, NULL);
+  }
+
+  if (priv->onreply != NULL)
+  {
+    if (dbus_connection_send_with_reply(
+          priv->connection,
+          priv->message, &priv->pending_reply, -1))
+    {
+      dbus_pending_call_set_notify(priv->pending_reply, pending_call_notify,
+                                   priv, NULL);
+    } else {
+      // TODO: set exception
+      g_warning("Failed to send message to %s", priv->destination);
+      call_onerror(context, priv, NULL);
+    }
+  } else {
+    dbus_message_set_no_reply(priv->message, TRUE);
+
+    if (!dbus_connection_send(priv->connection, priv->message, &serial))
+    {
+      // TODO: set exception
+      call_onerror(context, priv, NULL);
+    }
+  }
+  
+  return JSValueMakeUndefined(context);
+}
+
+static
+void call_onreply(JSContextRef context,
+                  MethodPrivate *priv,
+                  DBusMessage *message)
+{
+  if (priv->onreply == NULL)
+  {
+    return;
+  }
+
+  call_function_with_message_args(context, priv->this, priv->onreply, message);
+}
+
+static
+void call_onerror(JSContextRef context,
+                  MethodPrivate *priv,
+                  DBusMessage *message)
+{
+  if (priv->onerror == NULL)
+  {
+    return;
+  }
+
+  if (message == NULL)
+  {
+    /* We couldn't send the message */
+    JSStringRef name = JSStringCreateWithUTF8CString("MessageError");
+    JSStringRef str = JSStringCreateWithUTF8CString("Could not send message");
+    JSValueRef *args = g_new(JSValueRef, 2);
+    
+    args[0] = JSValueMakeString(context, name);
+    args[1] = JSValueMakeString(context, str);
+    
+    JSObjectCallAsFunction(context, priv->onerror, priv->this,
+                           2, args, NULL);
+    JSStringRelease(name);
+    JSStringRelease(str);
+    return;
+  }
+
+  call_function_with_message_args(context, priv->this, priv->onerror, message);
+}
+
+/*** Public API */
+
+/* NOTE: Takes ownership of the arguments! */
+JSObjectRef jscorebus_create_method (JSGlobalContextRef context,
+                                     DBusConnection *connection,
+                                     char *destination,
+                                     char *object_path,
+                                     char *method_name,
+                                     char *interface,
+                                     char *signature,
+                                     JSObjectRef thisObject,
+                                     JSValueRef* exception)
+{
+  MethodPrivate *priv = NULL;
+
+  g_return_val_if_fail(destination != NULL
+                       && object_path != NULL
+                       && method_name != NULL,
+                       NULL);
+
+  priv = g_new0(MethodPrivate, 1);
+
+  /* TODO: Maybe these should be JSStrings after all, to avoid copies? */
+  priv->destination = destination;
+  priv->object_path = object_path;
+  priv->method_name = method_name;
+
+  priv->interface = interface;
+  priv->signature = signature;
+
+  priv->async = true;
+
+  priv->connection = dbus_connection_ref(connection);
+  
+  priv->context = context;
+
+  priv->this = thisObject;
+
+  return JSObjectMake(context, get_class(), priv);
+}
diff --git a/jscorebus/jscorebus-method.h b/jscorebus/jscorebus-method.h
new file mode 100644 (file)
index 0000000..fc0da1f
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __JSCOREBUS_METHOD_H__
+#define __JSCOREBUS_METHOD_H__
+
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+/* NOTE: Takes ownership of the arguments! */
+JSObjectRef jscorebus_create_method (JSGlobalContextRef context,
+                                     DBusConnection *connection,
+                                     char *destination,
+                                     char *object_path,
+                                     char *method_name,
+                                     char *interface,
+                                     char *signature,
+                                     JSObjectRef thisObject,
+                                     JSValueRef* exception);
+
+#endif /* __JSCOREBUS_METHOD_H__ */
+
diff --git a/jscorebus/jscorebus-signal.c b/jscorebus/jscorebus-signal.c
new file mode 100644 (file)
index 0000000..31e6561
--- /dev/null
@@ -0,0 +1,369 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include "jscorebus-marshal.h"
+
+/* Private data for signals */
+typedef struct _SignalPrivate
+{
+  char *interface;
+  char *signal_name;
+  char *sender;
+  char *object_path;
+
+  DBusConnection *connection;
+  char *match_rule;
+
+  JSGlobalContextRef context;
+  JSObjectRef this;
+  JSObjectRef onemit;
+  gboolean enabled;
+} SignalPrivate;
+
+/* Utility functions */
+static
+JSClassRef get_class ();
+
+static
+void add_match_and_handler(SignalPrivate *priv);
+static
+void remove_match_and_handler(SignalPrivate *priv);
+
+/* JSCore methods */
+static
+void signal_finalize(JSObjectRef object);
+
+static
+bool signal_set_property(JSContextRef context,
+                         JSObjectRef object,
+                         JSStringRef propertyName,
+                         JSValueRef value,
+                         JSValueRef* exception);
+
+/* We keep a hash table to track the signals callbacks */
+static GHashTable *signal_hash;
+
+/* The Signal Class */
+static const
+JSClassDefinition signal_jsclass_def =
+{
+  0,
+  kJSClassAttributeNone,
+  "DBusSignal",
+  NULL,
+
+  NULL,
+  NULL,
+  
+  NULL, /* Initialize */
+  signal_finalize,
+  
+  NULL,
+  NULL,
+  signal_set_property, /* SetProperty */
+  NULL,
+  NULL,
+  
+  NULL, /* CallAsFunction */
+  NULL, /* Constructor */
+  NULL,
+  NULL
+};
+
+/*** JSCore methods */
+
+static
+void signal_finalize(JSObjectRef object)
+{
+  SignalPrivate *priv = (SignalPrivate *)JSObjectGetPrivate(object);
+
+  if (priv != NULL)
+  {
+    remove_match_and_handler(priv);
+    
+    g_free(priv->object_path);
+    g_free(priv->signal_name);
+    g_free(priv->interface);
+    g_free(priv->sender);
+    g_free(priv->object_path);
+    g_free(priv->match_rule);
+    dbus_connection_unref(priv->connection);
+
+    priv->this = NULL;
+    priv->onemit = NULL;
+    priv->context = NULL;
+    
+    g_free(priv);
+    priv = NULL;
+  }
+}
+
+static
+bool signal_set_property(JSContextRef context,
+                         JSObjectRef object,
+                         JSStringRef propertyName,
+                         JSValueRef value,
+                         JSValueRef* exception)
+{
+  SignalPrivate *priv;
+
+  priv = (SignalPrivate *)JSObjectGetPrivate(object);
+  g_assert(priv != NULL);
+
+  if (JSStringIsEqualToUTF8CString(propertyName, "enabled"))
+  {
+    /* We don't enable unless we have a callback */
+    if (priv->onemit == NULL)
+    {
+      /* TODO: Throw exception */
+      return TRUE;
+    }
+    if (JSValueIsBoolean(context, value))
+    {
+      priv->enabled = JSValueToBoolean(context, value);
+      if (priv->enabled)
+      {
+        add_match_and_handler(priv);
+      }
+      return TRUE;
+    }
+    
+    /* TODO: set exception */
+    g_warning("Tried to set a non-boolean to 'enabled'");
+    return TRUE;
+  } else if (JSStringIsEqualToUTF8CString(propertyName, "onemit")) {
+    priv->onemit = function_from_jsvalue(context, value, exception);
+
+    /* If the callback function goes away, we need to disable the signal,
+      * remove match and remove our entry from the handlers
+      */
+    if (priv->onemit == NULL)
+    {
+      remove_match_and_handler(priv);
+    }
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/*** Utility functions */
+
+static
+JSClassRef get_class ()
+{
+  JSClassRef jsclass = (JSClassRef)jsclass_lookup(&signal_jsclass_def);
+  
+  if (G_LIKELY(jsclass != NULL))
+  {
+    return jsclass;
+  }
+  
+  jsclassdef_insert("DBusSignal", &signal_jsclass_def);
+  jsclass = (JSClassRef)jsclass_lookup(&signal_jsclass_def);
+
+  g_assert(jsclass != NULL);
+
+  return jsclass;
+}
+
+static
+void add_match_and_handler(SignalPrivate *priv)
+{
+  char *signal_str;
+  GPtrArray *handlers;
+
+  dbus_bus_add_match(priv->connection, priv->match_rule, NULL);
+
+  signal_str = g_strdup_printf("%s.%s", priv->interface, priv->signal_name);
+  handlers = g_hash_table_lookup(signal_hash, signal_str);
+
+  if (handlers == NULL)
+  {
+    handlers = g_ptr_array_new();
+    g_hash_table_insert(signal_hash, signal_str, handlers);
+  } else {
+    g_free(signal_str);
+  }
+
+  g_ptr_array_add(handlers, priv);
+}
+
+static
+void remove_match_and_handler(SignalPrivate *priv)
+{
+  char *signal_str;
+  GPtrArray *handlers;
+
+  signal_str = g_strdup_printf("%s.%s", priv->interface, priv->signal_name);
+
+  handlers = g_hash_table_lookup(signal_hash, signal_str);
+
+  if (handlers != NULL)
+  {
+    g_ptr_array_remove(handlers, priv);
+    if (handlers->len == 0)
+    {
+      g_hash_table_remove(signal_hash, signal_str);
+      g_ptr_array_free(handlers, TRUE);
+    }
+  }
+
+  g_free(signal_str);
+
+  dbus_bus_remove_match(priv->connection, priv->match_rule, NULL);
+
+}
+
+static
+void call_onemit(SignalPrivate *priv,
+                 DBusMessage *message)
+{
+
+  if (priv->enabled == FALSE
+   || priv->onemit == NULL)
+  {
+    return;
+  }
+
+  /* If the signal was created with a sender, we need to check for it
+   * and not deliver if it does not match
+   */
+  if (priv->sender != NULL)
+  {
+    if (!dbus_message_has_sender(message, priv->sender))
+    {
+      return;
+    }
+  }
+  /* Ditto for object paths */
+  if (priv->object_path != NULL)
+  {
+    if (!dbus_message_has_path(message, priv->object_path))
+    {
+      return;
+    }
+  }
+
+  call_function_with_message_args(priv->context, priv->this, priv->onemit, message);
+}
+
+static
+DBusHandlerResult signal_filter(DBusConnection *connection,
+                                DBusMessage *message,
+                                void *user_data)
+{
+  char *signal_str;
+  GPtrArray *handlers;
+
+  if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+  {
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  signal_str = g_strdup_printf("%s.%s",
+                               dbus_message_get_interface(message),
+                               dbus_message_get_member(message));
+
+  handlers = g_hash_table_lookup(signal_hash, signal_str);
+  g_free(signal_str);
+  
+  if (handlers == NULL || handlers->len == 0)
+  {
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  g_ptr_array_foreach(handlers, (GFunc)call_onemit, message);
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/*** Public API */
+
+/* NOTE: Takes ownership of the arguments! */
+JSObjectRef jscorebus_create_signal (JSGlobalContextRef context,
+                                     DBusConnection *connection,
+                                     char *interface,
+                                     char *signal_name,
+                                     char *sender,
+                                     char *object_path,
+                                     JSObjectRef thisObject,
+                                     JSValueRef* exception)
+{
+  int i;
+  char **matchv;
+  char *signal_str;
+  GPtrArray *handlers;
+  SignalPrivate *priv = NULL;
+  static gboolean filter_added = FALSE;
+
+  g_return_val_if_fail(interface != NULL
+                       && signal_name != NULL,
+                       NULL);
+
+  priv = g_new0(SignalPrivate, 1);
+
+  priv->object_path = object_path;
+  priv->interface = interface;
+  priv->signal_name = signal_name;
+  priv->sender = sender;
+  priv->object_path = object_path;
+
+  priv->enabled = false;
+
+  priv->connection = dbus_connection_ref(connection);
+  
+  priv->context = context;
+  priv->this = thisObject;
+
+  /* If we don't already have a filter function for the signals, add one */
+  if (G_UNLIKELY(!filter_added))
+  {
+    signal_hash = g_hash_table_new(g_str_hash, g_str_equal);
+    dbus_connection_add_filter(connection, signal_filter, NULL, NULL);
+    filter_added = TRUE;
+  }
+
+  /* Add the match rule for the signal */
+  matchv = g_new0(char*, 4);
+  i = 0;
+  matchv[i++] = g_strdup_printf("type=signal,interface=%s,member=%s",
+                                priv->interface, priv->signal_name);
+  if (priv->sender != NULL)
+  {
+    matchv[i++] = g_strdup_printf("sender=%s", priv->sender);
+  }
+  if (priv->object_path != NULL)
+  {
+    matchv[i++] = g_strdup_printf("object_path=%s", priv->object_path);
+  }
+
+  priv->match_rule = g_strjoinv(",", matchv);
+  g_strfreev(matchv);
+  
+  return JSObjectMake(context, get_class(), priv);
+}
+
diff --git a/jscorebus/jscorebus-signal.h b/jscorebus/jscorebus-signal.h
new file mode 100644 (file)
index 0000000..c0289b1
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __JSCOREBUS_SIGNAL_H__
+#define __JSCOREBUS_SIGNAL_H__
+
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+/* NOTE: Takes ownership of the arguments! */
+JSObjectRef jscorebus_create_signal (JSGlobalContextRef context,
+                                     DBusConnection *connection,
+                                     char *interface,
+                                     char *signal_name,
+                                     char *sender,
+                                     char *object_path,
+                                     JSObjectRef thisObject,
+                                     JSValueRef* exception);
+
+#endif /* __JSCOREBUS_SIGNAL_H__ */
+
diff --git a/jscorebus/jscorebus-signature.c b/jscorebus/jscorebus-signature.c
new file mode 100644 (file)
index 0000000..5acdf29
--- /dev/null
@@ -0,0 +1,263 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include "jscorebus-classfactory.h"
+#include "jscorebus-marshal.h"
+
+gboolean jsvalue_typeof(JSContextRef context,
+                        JSValueRef jsvalue,
+                        const char *type)
+{
+  const JSClassDefinition *jsclassdef;
+  JSClassRef jsclass;
+  
+  jsclassdef = jsclassdef_lookup(type);
+  if (G_UNLIKELY(jsclassdef == NULL))
+    return FALSE;
+  jsclass = jsclass_lookup(jsclassdef);
+
+  return JSValueIsObjectOfClass(context, jsvalue, jsclass);
+}
+
+gboolean jsvalue_instanceof(JSContextRef context,
+                            JSValueRef jsvalue,
+                            const char *constructor)
+{
+  JSStringRef property;
+  JSObjectRef ctor;
+
+  property = JSStringCreateWithUTF8CString(constructor);
+  ctor = JSValueToObject(context,
+                         JSObjectGetProperty(context,
+                                             JSContextGetGlobalObject(context),
+                                             property,
+                                             NULL),
+                         NULL);
+  JSStringRelease(property);
+
+  return JSValueIsInstanceOfConstructor(context, jsvalue, ctor, NULL);
+}
+
+char *jsvalue_to_signature(JSContextRef context,
+                           JSValueRef jsvalue)
+{
+  char *signature = NULL;
+  
+  switch (JSValueGetType(context, jsvalue))
+  {
+    case kJSTypeBoolean:
+      {
+        signature = g_strdup(DBUS_TYPE_BOOLEAN_AS_STRING);
+        break;
+      }
+    case kJSTypeNumber:
+      {
+        /* JavaScript numbers are always doubles */
+        signature = g_strdup(DBUS_TYPE_DOUBLE_AS_STRING);
+        break;
+      }
+    case kJSTypeString:
+      {
+        signature = g_strdup(DBUS_TYPE_STRING_AS_STRING);
+        break;
+      }
+    case kJSTypeObject:
+      {
+        int i;
+        char *dict_signature = NULL;
+        JSPropertyNameArrayRef propnames;
+
+        /* Check for number types */
+        for (i = 0; i < JSCOREBUS_N_NUMBER_CLASSES; i++)
+        {
+          if (jsvalue_typeof(context, jsvalue, jscorebus_number_class_names[i]))
+          {
+            switch (jscorebus_number_class_types[i])
+            {
+#define NUMBER_SIGNATURE(t) \
+              case DBUS_TYPE_## t: \
+                signature = g_strdup(DBUS_TYPE_## t ##_AS_STRING); \
+                break;
+              NUMBER_SIGNATURE(UINT32)
+              NUMBER_SIGNATURE(INT32)
+              NUMBER_SIGNATURE(BYTE)
+              NUMBER_SIGNATURE(UINT64)
+              NUMBER_SIGNATURE(INT64)
+              NUMBER_SIGNATURE(UINT16)
+              NUMBER_SIGNATURE(INT16)
+
+              default:
+                break;
+            }
+          }
+        }
+
+        /* Check for arrays */
+        if (jsvalue_instanceof(context, jsvalue, "Array"))
+        {
+          JSPropertyNameArrayRef propnames;
+          char *array_signature;
+
+          propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)jsvalue);
+          if (!jsarray_get_signature(context, jsvalue, propnames, &array_signature))
+          { 
+            g_warning("Could not create array signature");
+            break;
+          }
+          signature = g_strdup_printf("a%s", array_signature);
+          g_free(array_signature);
+          break;
+        }
+
+        /* Check variants */
+        if (jsvalue_typeof(context, jsvalue, "DBusVariant"))
+        {
+          signature = g_strdup("v");
+          break;
+        }
+
+        if (jsvalue_typeof(context, jsvalue, "DBusObjectPath"))
+        {
+          signature = g_strdup(DBUS_TYPE_OBJECT_PATH_AS_STRING);
+          break;
+        }
+
+        if (jsvalue_typeof(context, jsvalue, "DBusSignature"))
+        {
+          signature = g_strdup(DBUS_TYPE_SIGNATURE_AS_STRING);
+          break;
+        }
+
+        /* Check structs */
+        if (jsvalue_typeof(context, jsvalue, "DBusStruct"))
+        {
+          JSPropertyNameArrayRef propnames;
+          JSObjectRef value = (JSObjectRef)JSObjectGetPrivate((JSObjectRef)jsvalue);
+          propnames = JSObjectCopyPropertyNames(context, value);
+          jsstruct_get_signature(context, value, propnames, &signature);
+          break;
+        }
+
+        /* Default conversion is to dict */
+        propnames = JSObjectCopyPropertyNames(context, jsvalue);
+        jsdict_get_signature(context, jsvalue, propnames, &dict_signature);
+        if (dict_signature != NULL)
+        {
+          signature = g_strdup_printf("a%s", dict_signature);
+          g_free(dict_signature);
+        }
+
+        break;
+      }
+    case kJSTypeUndefined:
+    case kJSTypeNull:
+    default:
+      g_warning("Signature lookup failed for unsupported type %i", JSValueGetType(context, jsvalue));
+      break;
+  }
+  return signature;
+}
+
+gboolean
+jsarray_get_signature(JSContextRef context,
+                      JSValueRef jsvalue,
+                      JSPropertyNameArrayRef propNames,
+                      char **signature)
+{
+  int i, props;
+  
+  *signature = NULL;
+  props = JSPropertyNameArrayGetCount(propNames);
+  /* Arrays are restricted to single complete types so we only need to look
+   * at the first property
+   */
+  if (props > 0)
+  {
+    *signature = jsvalue_to_signature(context,
+      JSObjectGetPropertyAtIndex(context, (JSObjectRef)jsvalue, 0, NULL));
+  }
+  return *signature == NULL ? FALSE : TRUE;
+}
+
+gboolean
+jsdict_get_signature(JSContextRef context,
+                     JSValueRef jsvalue,
+                     JSPropertyNameArrayRef propNames,
+                     char **signature)
+{
+  int i, props;
+  
+  *signature = NULL;
+  props = JSPropertyNameArrayGetCount(propNames);
+  /* Dicts support only string keys currently, though numbers would be another
+   * possibility...
+   */
+  if (props > 0)
+  {
+    char **signatures = g_new0(char*, 5);
+    
+    signatures[0] = g_strdup(DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING);
+    signatures[1] = g_strdup(DBUS_TYPE_STRING_AS_STRING);
+    signatures[2] = jsvalue_to_signature(context,
+      JSObjectGetProperty(context, (JSObjectRef)jsvalue,
+        JSPropertyNameArrayGetNameAtIndex(propNames, 0), NULL));
+    signatures[3] = g_strdup(DBUS_DICT_ENTRY_END_CHAR_AS_STRING);
+
+    *signature = g_strjoinv(NULL, signatures);
+    g_strfreev(signatures);
+  }
+  return *signature == NULL ? FALSE : TRUE;
+}
+
+gboolean
+jsstruct_get_signature(JSContextRef context,
+                       JSValueRef jsvalue,
+                       JSPropertyNameArrayRef propNames,
+                       char **signature)
+{
+  int props;
+  
+  *signature = NULL;
+  props = JSPropertyNameArrayGetCount(propNames);
+  if (props > 0)
+  {
+    char **signatures = g_new0(char*, props + 2);
+    int i = 0;
+    signatures[i] = g_strdup(DBUS_STRUCT_BEGIN_CHAR_AS_STRING);
+    while (i < props)
+    {
+      signatures[i+1] = jsvalue_to_signature(context,
+        JSObjectGetProperty(context, (JSObjectRef)jsvalue,
+          JSPropertyNameArrayGetNameAtIndex(propNames, i++), NULL));
+    }
+    signatures[props + 1] = g_strdup(DBUS_STRUCT_END_CHAR_AS_STRING);
+
+    *signature = g_strjoinv(NULL, signatures);
+    g_strfreev(signatures);
+  }
+  return *signature == NULL ? FALSE : TRUE;
+}
+
diff --git a/jscorebus/jscorebus.c b/jscorebus/jscorebus.c
new file mode 100644 (file)
index 0000000..a1e38b7
--- /dev/null
@@ -0,0 +1,490 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include "jscorebus-marshal.h"
+#include "jscorebus-method.h"
+#include "jscorebus-signal.h"
+
+/* Globals */
+static DBusConnection *session;
+static DBusConnection *system;
+
+
+/* Getters for the bus type properties */
+static
+JSValueRef _get_bus_type (JSContextRef context,
+                          JSObjectRef object,
+                          JSStringRef propertyName,
+                          JSValueRef *exception)
+{
+  if (JSStringIsEqualToUTF8CString(propertyName, "SESSION"))
+  {
+    return JSValueMakeNumber(context, DBUS_BUS_SESSION);
+  }
+  if (JSStringIsEqualToUTF8CString(propertyName, "SYSTEM"))
+  {
+    return JSValueMakeNumber(context, DBUS_BUS_SYSTEM);
+  }
+  
+  return JSValueMakeUndefined(context);
+}
+
+/* Static members */
+static const
+JSStaticValue dbus_jsclass_staticvalues[] = 
+{
+  { "SESSION", _get_bus_type, NULL, kJSPropertyAttributeReadOnly },
+  { "SYSTEM",  _get_bus_type, NULL, kJSPropertyAttributeReadOnly },
+  { NULL, NULL, NULL, 0 }
+};
+
+static
+void _number_finalize (JSObjectRef object)
+{
+  g_free(JSObjectGetPrivate(object));
+}
+
+static inline
+JSValueRef _get_number_object (JSContextRef context,
+                               JSObjectRef function,
+                               JSObjectRef thisObject,
+                               size_t argumentCount,
+                               const JSValueRef arguments[],
+                               JSValueRef *exception,
+                               JSClassRef number_class)
+{
+  dbus_uint64_t *value;
+  
+  if (argumentCount != 1)
+  {
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+
+  /* dbus_uint64_t should be large enough to hold any number so we just
+   * carry the value in private data as a pointer to dbus_uint64_t.
+   * The object class tells us later which type it is.
+   */
+  value = g_new0(dbus_uint64_t, 1);
+  *value = (dbus_uint64_t)JSValueToNumber(context, arguments[0], NULL);
+
+  return JSObjectMake(context, number_class, value);
+}
+
+JSValueRef _convert_number_object(JSContextRef context,
+                                  JSObjectRef object,
+                                  JSType type,
+                                  JSValueRef* exception)
+{
+  /* FIXME: this isn't called at all... */
+  g_debug("%s(%p to %d)", __FUNCTION__, object, type);
+#if 0
+  switch (type)
+  {
+    case kJSTypeNumber:
+      {
+        dbus_uint64_t value = jsvalue_to_number_value(context, object, NULL);
+        return JSValueMakeNumber(context, (double)value);
+      }
+    case kJSTypeString:
+      {
+        JSValueRef jsvalue;
+        JSStringRef jsstr;
+        char *str;
+        dbus_uint64_t value = jsvalue_to_number_value(context, object, NULL);
+        str = g_strdup_printf("%llu", value);
+        jsstr = JSStringCreateWithUTF8CString(str);
+        g_free(str);
+        jsvalue = JSValueMakeString(context, jsstr);
+        JSStringRelease(jsstr);
+        return jsvalue;
+      }
+    default:
+      break;
+  }
+#endif
+  return NULL;
+}
+
+#define MAKE_NUMBER_CLASS_AND_GETTER(classname, shortname) \
+  static const JSClassDefinition shortname ##_jsclass_def = \
+  { \
+    0, kJSClassAttributeNone, classname, \
+    NULL, NULL, NULL, NULL, _number_finalize, NULL, NULL, \
+    NULL, NULL, NULL, NULL, NULL, NULL, _convert_number_object \
+  }; \
+  \
+  static JSValueRef _get_ ##shortname (JSContextRef context, \
+                                    JSObjectRef function, \
+                                    JSObjectRef thisObject, \
+                                    size_t argumentCount, \
+                                    const JSValueRef arguments[], \
+                                    JSValueRef *exception) \
+  { \
+    return _get_number_object(context, function, thisObject, \
+                              argumentCount, arguments, exception, \
+                              (JSClassRef)jsclass_lookup(&shortname ##_jsclass_def)); \
+  }
+
+MAKE_NUMBER_CLASS_AND_GETTER("DBusUInt32", uint32)
+MAKE_NUMBER_CLASS_AND_GETTER("DBusInt32", int32)
+MAKE_NUMBER_CLASS_AND_GETTER("DBusDouble", double)
+MAKE_NUMBER_CLASS_AND_GETTER("DBusByte", byte)
+MAKE_NUMBER_CLASS_AND_GETTER("DBusUInt64", uint64)
+MAKE_NUMBER_CLASS_AND_GETTER("DBusInt64", int64)
+MAKE_NUMBER_CLASS_AND_GETTER("DBusUInt16", uint16)
+MAKE_NUMBER_CLASS_AND_GETTER("DBusInt16", int16)
+
+static const JSClassDefinition variant_jsclass_def =
+{
+  0, kJSClassAttributeNone, "DBusVariant",
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static
+JSValueRef _construct_variant (JSContextRef context,
+                               JSObjectRef function,
+                               JSObjectRef thisObject,
+                               size_t argumentCount,
+                               const JSValueRef arguments[],
+                               JSValueRef *exception)
+{
+
+  if (argumentCount != 1)
+  {
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+
+  /* Just carry the value in private data */
+  return JSObjectMake(context, (JSClassRef)jsclass_lookup(&variant_jsclass_def), (void*)arguments[0]);
+}
+
+static const JSClassDefinition struct_jsclass_def =
+{
+  0, kJSClassAttributeNone, "DBusStruct",
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static
+JSValueRef _construct_struct (JSContextRef context,
+                              JSObjectRef function,
+                              JSObjectRef thisObject,
+                              size_t argumentCount,
+                              const JSValueRef arguments[],
+                              JSValueRef *exception)
+{
+  if (argumentCount != 1)
+  {
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+
+  /* Just carry the object in private data */
+  return JSObjectMake(context,
+                      (JSClassRef)jsclass_lookup(&struct_jsclass_def),
+                      (void*)arguments[0]);
+}
+
+static const JSClassDefinition object_path_jsclass_def =
+{
+  0, kJSClassAttributeNone, "DBusObjectPath",
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static
+gboolean is_valid_path (const char *path)
+{
+  const char *this = path;
+  const char *prev = this;
+  
+  if (strlen(path) == 0)
+    return FALSE;
+  
+  /* MUST begin with zero */
+  if (*this++ != '/')
+    return FALSE;
+  
+  /* The path is guranteed to be null-terminated */
+  while (*this != '\0')
+  {
+    /* Two slashes can't be together */
+    if (*this == '/' && *prev == '/')
+    {
+      return FALSE;
+    } else if (!(((*this) >= '0' && (*this) <= '9') ||
+                 ((*this) >= 'A' && (*this) <= 'Z') ||
+                 ((*this) >= 'a' && (*this) <= 'z') ||
+                  (*this) == '_' || (*this) == '/')) {
+      return FALSE;
+    }
+    prev = this;
+    this++;
+  }
+  
+  return TRUE;
+}
+
+static
+JSValueRef _construct_object_path (JSContextRef context,
+                               JSObjectRef function,
+                               JSObjectRef thisObject,
+                               size_t argumentCount,
+                               const JSValueRef arguments[],
+                               JSValueRef *exception)
+{
+  const char *path;
+
+  if (argumentCount != 1)
+  {
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+
+  /* D-Bus doesn't like invalid object paths _at all_, so instead of risking
+   * disconnection, we'll validate the path now.
+   */
+  path = string_from_jsvalue(context, arguments[0]);
+  if (!is_valid_path(path))
+  {
+    g_free((gpointer)path);
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+  g_free((gpointer)path);
+
+  /* Just carry the value in private data */
+  return JSObjectMake(context,
+                      (JSClassRef)jsclass_lookup(&object_path_jsclass_def),
+                      (void*)arguments[0]);
+}
+
+static const JSClassDefinition signature_jsclass_def =
+{
+  0, kJSClassAttributeNone, "DBusSignature",
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static
+JSValueRef _construct_signature (JSContextRef context,
+                                 JSObjectRef function,
+                                 JSObjectRef thisObject,
+                                 size_t argumentCount,
+                                 const JSValueRef arguments[],
+                                 JSValueRef *exception)
+{
+  if (argumentCount != 1)
+  {
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+
+  /* Just carry the value in private data */
+  return JSObjectMake(context,
+                      (JSClassRef)jsclass_lookup(&signature_jsclass_def),
+                      (void*)arguments[0]);
+}
+
+static
+JSValueRef getMethod (JSContextRef context,
+                      JSObjectRef function,
+                      JSObjectRef thisObject,
+                      size_t argumentCount,
+                      const JSValueRef arguments[],
+                      JSValueRef *exception)
+{
+  JSGlobalContextRef global_context = JSObjectGetPrivate(thisObject);
+  
+  if (argumentCount < 4)
+  {
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+
+  return jscorebus_create_method(global_context,
+                                 JSValueToNumber(context, arguments[0], NULL)
+                                  == DBUS_BUS_SYSTEM ? system : session,
+                                 string_from_jsvalue(context, arguments[1]),
+                                 string_from_jsvalue(context, arguments[2]),
+                                 string_from_jsvalue(context, arguments[3]),
+                                 argumentCount > 4 ? 
+                                   string_from_jsvalue(context, arguments[4])
+                                   : NULL,
+                                 argumentCount > 5 ? 
+                                   string_from_jsvalue(context, arguments[5])
+                                   : NULL,
+                                 argumentCount > 6 ? 
+                                   JSValueToObject(context, arguments[6], NULL)
+                                   : NULL,
+                                 exception);
+}
+
+static
+JSValueRef getSignal (JSContextRef context,
+                      JSObjectRef function,
+                      JSObjectRef thisObject,
+                      size_t argumentCount,
+                      const JSValueRef arguments[],
+                      JSValueRef *exception)
+{
+  JSGlobalContextRef global_context = JSObjectGetPrivate(thisObject);
+
+  if (argumentCount < 3)
+  {
+    /* TODO: set exception */
+    return JSValueMakeUndefined(context);
+  }
+
+  return jscorebus_create_signal(global_context,
+                                 JSValueToNumber(context, arguments[0], NULL)
+                                  == DBUS_BUS_SYSTEM ? system : session,
+                                 string_from_jsvalue(context, arguments[1]),
+                                 string_from_jsvalue(context, arguments[2]),
+                                 argumentCount > 3 ? 
+                                   string_from_jsvalue(context, arguments[3])
+                                   : NULL,
+                                 argumentCount > 4 ? 
+                                   string_from_jsvalue(context, arguments[4])
+                                   : NULL,
+                                 argumentCount > 5 ? 
+                                   JSValueToObject(context, arguments[5], NULL)
+                                   : NULL,
+                                 exception);
+}
+
+static
+void dbus_finalize(JSObjectRef object)
+{
+  g_debug(G_STRFUNC);
+  JSObjectSetPrivate(object, NULL);
+}
+
+static const
+JSStaticFunction dbus_jsclass_staticfuncs[] = 
+{
+  /* Type constructors */
+  { "Int32",   _get_int32, kJSPropertyAttributeReadOnly },
+  { "UInt32",  _get_uint32, kJSPropertyAttributeReadOnly },
+  { "Double",  _get_double, kJSPropertyAttributeReadOnly },
+  { "Byte",    _get_byte, kJSPropertyAttributeReadOnly },
+  { "Int64",   _get_int64, kJSPropertyAttributeReadOnly },
+  { "UInt64",  _get_uint64, kJSPropertyAttributeReadOnly },
+  { "Int16",   _get_int16, kJSPropertyAttributeReadOnly },
+  { "UInt16",  _get_uint16, kJSPropertyAttributeReadOnly },
+  { "ObjectPath", _construct_object_path, kJSPropertyAttributeReadOnly },
+  { "Signature", _construct_signature, kJSPropertyAttributeReadOnly },
+  { "Variant", _construct_variant, kJSPropertyAttributeReadOnly },
+  { "Struct", _construct_struct, kJSPropertyAttributeReadOnly },
+
+  /* Methods */
+  { "getMethod", getMethod, kJSPropertyAttributeReadOnly },
+  { "getSignal", getSignal, kJSPropertyAttributeReadOnly },
+  { NULL, NULL, 0 }
+};
+
+/* The DBus Class */
+static const
+JSClassDefinition dbus_jsclass_def =
+{
+  0,
+  kJSClassAttributeNone,
+  "DBus",
+  NULL,
+
+  dbus_jsclass_staticvalues,
+  dbus_jsclass_staticfuncs,
+  
+  NULL,
+  dbus_finalize,
+  
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+
+/**
+ * Public API
+ */
+void jscorebus_init(DBusConnection *psession, DBusConnection *psystem)
+{
+  session = psession;
+  system = psystem;
+
+#define INIT_NUMBER_CLASS(name, def, type, num) \
+  jsclassdef_insert(name, def); \
+  jscorebus_number_class_names[num] = name; \
+  jscorebus_number_class_types[num] = type;
+
+  INIT_NUMBER_CLASS("DBusInt32",  &int32_jsclass_def,  DBUS_TYPE_INT32,  0);
+  INIT_NUMBER_CLASS("DBusUInt32", &uint32_jsclass_def, DBUS_TYPE_UINT32, 1);
+  INIT_NUMBER_CLASS("DBusDouble", &double_jsclass_def, DBUS_TYPE_DOUBLE, 2);
+  INIT_NUMBER_CLASS("DBusByte",   &byte_jsclass_def,   DBUS_TYPE_BYTE,   3);
+  INIT_NUMBER_CLASS("DBusUInt64", &uint64_jsclass_def, DBUS_TYPE_UINT64, 4);
+  INIT_NUMBER_CLASS("DBusInt64",  &int64_jsclass_def,  DBUS_TYPE_INT64,  5);
+  INIT_NUMBER_CLASS("DBusUInt16", &uint16_jsclass_def, DBUS_TYPE_UINT16, 6);
+  INIT_NUMBER_CLASS("DBusInt16",  &int16_jsclass_def,  DBUS_TYPE_INT16,  7);
+
+  jsclassdef_insert("DBusObjectPath", &object_path_jsclass_def);
+  jsclassdef_insert("DBusSignature", &signature_jsclass_def);
+
+  jsclassdef_insert("DBusVariant", &variant_jsclass_def);
+  jsclassdef_insert("DBusStruct", &struct_jsclass_def);
+
+}
+
+void jscorebus_export(JSGlobalContextRef context)
+{
+  JSObjectRef globalObject;
+  JSObjectRef dbus;
+  JSStringRef jsstr;
+  JSClassRef dbus_jsclass;
+
+//  global_context = context;
+    
+  dbus_jsclass = JSClassCreate(&dbus_jsclass_def);
+  dbus = JSObjectMake(context, dbus_jsclass, context);
+
+  globalObject = JSContextGetGlobalObject(context);
+  jsstr = JSStringCreateWithUTF8CString("DBus");
+  JSObjectSetProperty(context, globalObject,
+                      jsstr, dbus,
+                      kJSPropertyAttributeNone, NULL);
+}
+
diff --git a/jscorebus/jscorebus.h b/jscorebus/jscorebus.h
new file mode 100644 (file)
index 0000000..d1c8806
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Browser D-Bus Bridge, JavaScriptCore version
+ *
+ * Copyright © 2008 Movial Creative Technologies Inc
+ *  Contact: Movial Creative Technologies Inc, <info@movial.com>
+ *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __JSCOREBUS_H___
+#define __JSCOREBUS_H___
+#include <dbus/dbus.h>
+#include <JavaScriptCore/JavaScript.h>
+
+/**
+ * Initialize the JSCoreBus bindings.
+ *
+ * Pass NULL if you wish to omit one of the connections.
+ */
+void jscorebus_init(DBusConnection *session, DBusConnection *system);
+
+/**
+ * Export the D-Bus object to the JavaScript execution context.
+ */
+void jscorebus_export(JSGlobalContextRef context);
+
+#endif /* __JSCOREBUS_H___ */
+
diff --git a/jscorebus/jscorebus.pc b/jscorebus/jscorebus.pc
new file mode 100644 (file)
index 0000000..d7a114e
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@PREFIX@
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: JSCoreBus
+Description: JavaScriptCore bindings for D-Bus
+Version: 0.1
+Requires: glib-2.0 gobject-2.0 dbus-1 dbus-glib-1 webkit-1.0
+Libs: -L${libdir} -ljscorebus
+Cflags: -I${includedir}