[gjs/wip/gcampax/70-arg-cache: 5/6] arg-cache: extend to handle interface types too



commit e9570cf02ffdcfd00846aeff6578feb0e42d4b54
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Mon Apr 22 18:28:52 2013 +0200

    arg-cache: extend to handle interface types too
    
    Handle enums, objects and boxed types through the argument cache
    rather than using the generic marshaller.

 gi/arg-cache.cpp | 471 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 gi/arg-cache.h   |  26 ++-
 2 files changed, 493 insertions(+), 4 deletions(-)
---
diff --git a/gi/arg-cache.cpp b/gi/arg-cache.cpp
index c94705a..72c81bd 100644
--- a/gi/arg-cache.cpp
+++ b/gi/arg-cache.cpp
@@ -32,7 +32,13 @@
 
 #include "arg.h"
 #include "arg-cache.h"
+#include "boxed.h"
 #include "function.h"
+#include "gerror.h"
+#include "gjs/byteArray.h"
+#include "object.h"
+#include "union.h"
+#include "value.h"
 
 static bool gjs_arg_cache_build_normal_in_arg(GjsArgumentCache *self,
                                               GITypeTag         tag);
@@ -724,6 +730,18 @@ report_primitive_type_mismatch(JSContext        *cx,
     return false;
 }
 
+static bool
+report_object_primitive_type_mismatch(JSContext        *cx,
+                                      GjsArgumentCache *self,
+                                      JS::Value         value,
+                                      GType             expected)
+{
+    gjs_throw(cx, "Expected an object of type %s for argument '%s' but got type %s",
+              g_type_name(expected), self->arg_name,
+              gjs_get_type_name(value));
+    return false;
+}
+
 static bool
 report_out_of_range(JSContext        *cx,
                     GjsArgumentCache *self,
@@ -933,6 +951,447 @@ gjs_marshal_string_in_release(JSContext            *cx,
     return true;
 }
 
+static bool
+gjs_marshal_enum_in_in(JSContext            *cx,
+                       GjsArgumentCache     *self,
+                       GjsFunctionCallState *state,
+                       GIArgument           *arg,
+                       JS::HandleValue       value)
+{
+    int64_t number;
+
+    if (!gjs_value_to_int64(cx, value, &number))
+        return false;
+
+    if (number > self->contents.enum_type.enum_max ||
+        number < self->contents.enum_type.enum_min) {
+        gjs_throw(cx, "%" G_GINT64_MODIFIER "d is not a valid value for enum argument %s",
+                  number, self->arg_name);
+        return false;
+    }
+
+    if (self->contents.enum_type.enum_max <= G_MAXINT32)
+        arg->v_int = number;
+    else if (self->contents.enum_type.enum_max <= G_MAXUINT32)
+        arg->v_uint = number;
+    else
+        arg->v_int64 = number;
+
+    return true;
+}
+
+static bool
+gjs_marshal_flags_in_in(JSContext            *cx,
+                        GjsArgumentCache     *self,
+                        GjsFunctionCallState *state,
+                        GIArgument           *arg,
+                        JS::HandleValue       value)
+{
+    int64_t number;
+
+    if (!gjs_value_to_int64(cx, value, &number))
+        return false;
+
+    if ((uint64_t(number) & self->contents.flags_mask) != uint64_t(number)) {
+        gjs_throw(cx, "%" G_GINT64_MODIFIER "d is not a valid value for enum argument %s",
+                  number, self->arg_name);
+        return false;
+    }
+
+    if (self->contents.flags_mask <= G_MAXUINT32)
+        arg->v_uint = number;
+    else
+        arg->v_uint64 = number;
+
+    return true;
+}
+
+static bool
+gjs_marshal_foreign_in_in(JSContext            *cx,
+                          GjsArgumentCache     *self,
+                          GjsFunctionCallState *state,
+                          GIArgument           *arg,
+                          JS::HandleValue       value)
+{
+    GIStructInfo *foreign_info;
+
+    foreign_info = (GIStructInfo*) g_type_info_get_interface(&self->type_info);
+    self->contents.tmp_foreign_info = foreign_info;
+    return gjs_struct_foreign_convert_to_g_argument(cx, value, foreign_info,
+                                                    self->arg_name,
+                                                    GJS_ARGUMENT_ARGUMENT,
+                                                    self->transfer,
+                                                    self->nullable, arg);
+}
+
+static bool
+gjs_marshal_foreign_in_release(JSContext            *cx,
+                               GjsArgumentCache     *self,
+                               GjsFunctionCallState *state,
+                               GIArgument           *in_arg,
+                               GIArgument           *out_arg)
+{
+    bool ok = true;
+
+    if (self->transfer == GI_TRANSFER_NOTHING)
+        ok = gjs_struct_foreign_release_g_argument(cx, self->transfer,
+                                                   self->contents.tmp_foreign_info,
+                                                   in_arg);
+
+    g_base_info_unref(self->contents.tmp_foreign_info);
+    return ok;
+}
+
+static bool
+gjs_marshal_gvalue_in_in(JSContext            *cx,
+                         GjsArgumentCache     *self,
+                         GjsFunctionCallState *state,
+                         GIArgument           *arg,
+                         JS::HandleValue       value)
+{
+    GValue gvalue = G_VALUE_INIT;
+
+    if (!gjs_value_to_g_value(cx, value, &gvalue))
+        return false;
+
+    arg->v_pointer = g_boxed_copy(G_TYPE_VALUE, &gvalue);
+
+    g_value_unset(&gvalue);
+    return true;
+}
+
+static bool
+gjs_marshal_boxed_in_in(JSContext            *cx,
+                        GjsArgumentCache     *self,
+                        GjsFunctionCallState *state,
+                        GIArgument           *arg,
+                        JS::HandleValue       value)
+{
+    if (value.isNull()) {
+        if (!self->nullable)
+            return report_invalid_null(cx, self);
+
+        arg->v_pointer = nullptr;
+        return true;
+    }
+
+    GType gtype = self->contents.object.gtype;
+
+    if (!value.isObject())
+        return report_object_primitive_type_mismatch(cx, self,
+                                                     value, gtype);
+
+    JS::RootedObject object(cx, &value.toObject());
+    if (gtype == G_TYPE_ERROR) {
+        if (!gjs_typecheck_gerror(cx, object, true))
+            return false;
+
+        arg->v_pointer = gjs_gerror_from_error(cx, object);
+    } else {
+        if (!gjs_typecheck_boxed(cx, object, self->contents.object.info,
+                                 gtype, true))
+            return false;
+
+        arg->v_pointer = gjs_c_struct_from_boxed(cx, object);
+    }
+
+    if (self->transfer != GI_TRANSFER_NOTHING) {
+        g_assert(gtype != G_TYPE_NONE);
+
+        if (gtype == G_TYPE_VARIANT)
+            g_variant_ref(static_cast<GVariant *>(arg->v_pointer));
+        else
+            arg->v_pointer = g_boxed_copy(gtype, arg->v_pointer);
+    }
+
+    return true;
+}
+
+/* Unions include ClutterEvent and GdkEvent, which occur fairly
+   often in an interactive application, so they're worth a special
+   case in a different virtual function
+*/
+static bool
+gjs_marshal_union_in_in(JSContext            *cx,
+                        GjsArgumentCache     *self,
+                        GjsFunctionCallState *state,
+                        GIArgument           *arg,
+                        JS::HandleValue       value)
+{
+    if (value.isNull()) {
+        if (!self->nullable)
+            return report_invalid_null(cx, self);
+
+        arg->v_pointer = nullptr;
+        return true;
+    }
+
+    GType gtype = self->contents.object.gtype;
+    g_assert(gtype != G_TYPE_NONE);
+
+    if (!value.isObject())
+        return report_object_primitive_type_mismatch(cx, self,
+                                                     value, gtype);
+
+    JS::RootedObject object(cx, &value.toObject());
+    if (!gjs_typecheck_union(cx, object, self->contents.object.info,
+                             gtype, true))
+        return false;
+
+    arg->v_pointer = gjs_c_union_from_union(cx, object);
+
+    if (self->transfer != GI_TRANSFER_NOTHING)
+        arg->v_pointer = g_boxed_copy(gtype, arg->v_pointer);
+
+    return true;
+}
+
+static bool
+gjs_marshal_gclosure_in_in(JSContext            *cx,
+                           GjsArgumentCache     *self,
+                           GjsFunctionCallState *state,
+                           GIArgument           *arg,
+                           JS::HandleValue       value)
+{
+    if (value.isNull()) {
+        if (!self->nullable)
+            return report_invalid_null(cx, self);
+
+        arg->v_pointer = nullptr;
+        return true;
+    }
+
+    if (!value.isObject())
+        return report_primitive_type_mismatch(cx, self, value, JSTYPE_FUNCTION);
+
+    JS::RootedObject object(cx, &value.toObject());
+    arg->v_pointer = gjs_closure_new_marshaled(cx, object, "boxed");
+    g_closure_ref(static_cast<GClosure *>(arg->v_pointer));
+    g_closure_sink(static_cast<GClosure *>(arg->v_pointer));
+
+    return true;
+}
+
+static bool
+gjs_marshal_gbytes_in_in(JSContext            *cx,
+                         GjsArgumentCache     *self,
+                         GjsFunctionCallState *state,
+                         GIArgument           *arg,
+                         JS::HandleValue       value)
+{
+    if (value.isNull()) {
+        if (!self->nullable)
+            return report_invalid_null(cx, self);
+
+        arg->v_pointer = nullptr;
+        return true;
+    }
+
+    if (!value.isObject())
+        return report_object_primitive_type_mismatch(cx, self,
+                                                     value, G_TYPE_BYTES);
+
+    JS::RootedObject object(cx, &value.toObject());
+    if (gjs_typecheck_bytearray(cx, object, false)) {
+        arg->v_pointer = gjs_byte_array_get_bytes(cx, object);
+    } else {
+        if (!gjs_typecheck_boxed(cx, object, self->contents.object.info,
+                                 G_TYPE_BYTES, true))
+            return false;
+
+        arg->v_pointer = gjs_c_struct_from_boxed(cx, object);
+
+        /* The bytearray path is taking an extra ref irrespective
+           of transfer ownership, so we need to do the same here.
+        */
+        g_bytes_ref(static_cast<GBytes *>(arg->v_pointer));
+    }
+
+    return true;
+}
+
+static bool
+gjs_marshal_object_in_in(JSContext            *cx,
+                         GjsArgumentCache     *self,
+                         GjsFunctionCallState *state,
+                         GIArgument           *arg,
+                         JS::HandleValue       value)
+{
+    if (value.isNull()) {
+        if (!self->nullable)
+            return report_invalid_null(cx, self);
+
+        arg->v_pointer = nullptr;
+        return true;
+    }
+
+    GType gtype = self->contents.object.gtype;
+    g_assert(gtype != G_TYPE_NONE);
+
+    if (!value.isObject())
+        return report_object_primitive_type_mismatch(cx, self,
+                                                     value, gtype);
+
+    JS::RootedObject object(cx, &value.toObject());
+    if (!gjs_typecheck_object(cx, object, gtype, true))
+        return false;
+
+    arg->v_pointer = gjs_g_object_from_object(cx, object);
+
+    if (self->transfer != GI_TRANSFER_NOTHING)
+        g_object_ref(arg->v_pointer);
+
+    return true;
+}
+
+static bool
+gjs_marshal_boxed_in_release(JSContext            *cx,
+                             GjsArgumentCache     *self,
+                             GjsFunctionCallState *state,
+                             GIArgument           *in_arg,
+                             GIArgument           *out_arg)
+{
+    GType gtype = self->contents.object.gtype;
+    g_assert(g_type_is_a(gtype, G_TYPE_BOXED));
+
+    g_boxed_free(gtype, in_arg->v_pointer);
+    return true;
+}
+
+static void
+gjs_arg_cache_build_enum_bounds(GjsArgumentCache *self,
+                                GIEnumInfo       *enum_info)
+{
+    int64_t min = G_MAXINT64;
+    int64_t max = G_MININT64;
+    int n = g_enum_info_get_n_values(enum_info);
+    for (int i = 0; i < n; i++) {
+        GIValueInfo *value_info = g_enum_info_get_value(enum_info, i);
+        int64_t value = g_value_info_get_value(value_info);
+
+        if (value > max)
+            max = value;
+        if (value < min)
+            min = value;
+
+        g_base_info_unref(value_info);
+    }
+
+    self->contents.enum_type.enum_min = min;
+    self->contents.enum_type.enum_max = max;
+}
+
+static void
+gjs_arg_cache_build_flags_mask(GjsArgumentCache *self,
+                               GIEnumInfo       *enum_info)
+{
+    uint64_t mask = 0;
+    int n = g_enum_info_get_n_values(enum_info);
+    for (int i = 0; i < n; i++) {
+        GIValueInfo *value_info = g_enum_info_get_value(enum_info, i);
+        uint64_t value = (guint64) g_value_info_get_value(value_info);
+        mask |= value;
+
+        g_base_info_unref(value_info);
+    }
+
+    self->contents.flags_mask = mask;
+}
+
+static bool
+gjs_arg_cache_build_interface_in_arg(GjsArgumentCache *self)
+{
+    GIBaseInfo *interface_info = g_type_info_get_interface(&self->type_info);
+    GIInfoType interface_type = g_base_info_get_type(interface_info);
+    bool ok = true;
+    GType gtype;
+
+    /* We do some transfer magic later, so lets ensure we
+       don't mess up. Should not happen in practice. */
+    if (G_UNLIKELY (self->transfer == GI_TRANSFER_CONTAINER))
+        return false;
+
+    switch (interface_type) {
+    case GI_INFO_TYPE_ENUM:
+        gjs_arg_cache_build_enum_bounds(self, (GIEnumInfo*) interface_info);
+        self->marshal_in = gjs_marshal_enum_in_in;
+        break;
+
+    case GI_INFO_TYPE_FLAGS:
+        gjs_arg_cache_build_flags_mask(self, (GIEnumInfo*) interface_info);
+        self->marshal_in = gjs_marshal_flags_in_in;
+        break;
+
+    case GI_INFO_TYPE_STRUCT:
+        if (g_struct_info_is_foreign((GIStructInfo*)interface_info)) {
+            self->marshal_in = gjs_marshal_foreign_in_in;
+            self->release = gjs_marshal_foreign_in_release;
+            break;
+        } else {
+            /* fall through */
+        }
+    case GI_INFO_TYPE_BOXED:
+    case GI_INFO_TYPE_OBJECT:
+    case GI_INFO_TYPE_INTERFACE:
+    case GI_INFO_TYPE_UNION:
+        gtype = g_registered_type_info_get_g_type
+            ((GIRegisteredTypeInfo*)interface_info);
+        self->contents.object.gtype = gtype;
+        self->contents.object.info = interface_info;
+        g_base_info_ref(self->contents.object.info);
+
+        /* Transfer handling is a bit complex here, because
+           some of our _in marshallers know not to copy stuff if we don't
+           need to.
+        */
+
+        if (gtype == G_TYPE_VALUE) {
+            self->marshal_in = gjs_marshal_gvalue_in_in;
+            if (self->transfer == GI_TRANSFER_NOTHING)
+                self->release = gjs_marshal_boxed_in_release;
+        } else if (gtype == G_TYPE_CLOSURE) {
+            self->marshal_in = gjs_marshal_gclosure_in_in;
+            if (self->transfer == GI_TRANSFER_NOTHING)
+                self->release = gjs_marshal_boxed_in_release;
+        } else if (gtype == G_TYPE_BYTES) {
+            self->marshal_in = gjs_marshal_gbytes_in_in;
+            if (self->transfer == GI_TRANSFER_NOTHING)
+                self->release = gjs_marshal_boxed_in_release;
+        } else if (g_type_is_a(gtype, G_TYPE_OBJECT) ||
+                   g_type_is_a(gtype, G_TYPE_INTERFACE)) {
+            self->marshal_in = gjs_marshal_object_in_in;
+            /* This is a smart marshaller, no release needed */
+        } else if (interface_type == GI_INFO_TYPE_UNION) {
+            if (gtype != G_TYPE_NONE) {
+                self->marshal_in = gjs_marshal_union_in_in;
+                /* This is a smart marshaller, no release needed */
+            } else {
+                /* Can't handle unions without a GType */
+                ok = false;
+            }
+        } else { /* generic boxed type */
+            if (gtype == G_TYPE_NONE &&
+                self->transfer != GI_TRANSFER_NOTHING) {
+                /* Can't transfer ownership of a structure type not registered as a boxed */
+                ok = false;
+            } else {
+                self->marshal_in = gjs_marshal_boxed_in_in;
+                /* This is a smart marshaller, no release needed */
+            }
+        }
+        break;
+
+    default:
+        /* Don't know how to handle this interface type
+           (should not happen in practice, for typelibs emitted
+           by g-ir-compiler) */
+        ok = false;
+    }
+
+    g_base_info_unref(interface_info);
+    return ok;
+}
+
 static bool
 gjs_arg_cache_build_normal_in_arg(GjsArgumentCache *self,
                                   GITypeTag         tag)
@@ -997,18 +1456,26 @@ gjs_arg_cache_build_normal_in_arg(GjsArgumentCache *self,
 
     case GI_TYPE_TAG_FILENAME:
         self->marshal_in = gjs_marshal_string_in_in;
-        if (self->transfer != GI_TRANSFER_NOTHING)
+        if (self->transfer == GI_TRANSFER_NOTHING)
             self->release = gjs_marshal_string_in_release;
         self->contents.string_is_filename = true;
         break;
 
     case GI_TYPE_TAG_UTF8:
         self->marshal_in = gjs_marshal_string_in_in;
-        if (self->transfer != GI_TRANSFER_NOTHING)
+        if (self->transfer == GI_TRANSFER_NOTHING)
             self->release = gjs_marshal_string_in_release;
         self->contents.string_is_filename = false;
         break;
 
+    case GI_TYPE_TAG_INTERFACE:
+        return gjs_arg_cache_build_interface_in_arg(self);
+
+    case GI_TYPE_TAG_ARRAY:
+    case GI_TYPE_TAG_GLIST:
+    case GI_TYPE_TAG_GSLIST:
+    case GI_TYPE_TAG_GHASH:
+    case GI_TYPE_TAG_ERROR:
     default:
         /* FIXME */
         /* Falling back to the generic marshaller */
diff --git a/gi/arg-cache.h b/gi/arg-cache.h
index 75c2d18..aee029f 100644
--- a/gi/arg-cache.h
+++ b/gi/arg-cache.h
@@ -61,22 +61,44 @@ typedef struct _GjsArgumentCache {
     bool is_return : 1;
 
     union {
+        /* for explicit array only */
         struct {
             int length_pos;
             GITypeTag length_tag;
         } array;
+
         struct {
             GIScopeType scope;
             int closure_pos;
             int destroy_pos;
         } callback;
+
         struct {
             GITypeTag number_tag;
             unsigned is_unsigned : 1;
         } number;
-        size_t caller_allocates_size;
+
+        /* boxed / union / GObject */
+        struct {
+            GType gtype;
+            GIBaseInfo *info;
+        } object;
+
+        /* foreign structures */
+        GIStructInfo *tmp_foreign_info;
+
+        /* enum / flags */
+        struct {
+            int64_t enum_min;
+            int64_t enum_max;
+        } enum_type;
+        uint64_t flags_mask;
+
+        /* string / filename */
         unsigned string_is_filename : 1;
-        int dummy;
+
+        /* out caller allocates (FIXME: should be in object) */
+        size_t caller_allocates_size;
     } contents;
 } GjsArgumentCache;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]