[gjs/wip/gcampax/70-arg-cache: 35/38] arg-cache: be more fine-grained in the argument cache



commit ba5e7a1ee0f6e7b11a796b3628046d081572750a
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sun Apr 21 20:20:32 2013 +0200

    arg-cache: be more fine-grained in the argument cache
    
    Use different virtual functions for different argument types, so
    that conditions and switches need only be evaluated once, and
    store useful information in the argument cache structure.
    
    (Philip Chimento: rebased and fixed coding style.)

 gi/arg-cache.cpp | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 gi/arg-cache.h   |   8 ++
 2 files changed, 289 insertions(+), 3 deletions(-)
---
diff --git a/gi/arg-cache.cpp b/gi/arg-cache.cpp
index 73d9e93b..5a723afd 100644
--- a/gi/arg-cache.cpp
+++ b/gi/arg-cache.cpp
@@ -23,14 +23,17 @@
 
 #include <config.h>
 
+#include <stdint.h>
 #include <string.h>
 
 #include <ffi.h>
 #include <girepository.h>
 #include <glib.h>
 
+#include <js/Conversions.h>
 #include <js/RootingAPI.h>
 #include <js/TypeDecls.h>
+#include <js/Utility.h>  // for UniqueChars
 #include <jsapi.h>        // for JS_TypeOfValue
 #include <jsfriendapi.h>  // for JS_GetObjectFunction
 #include <jspubtd.h>      // for JSTYPE_FUNCTION
@@ -38,6 +41,7 @@
 #include "gi/arg-cache.h"
 #include "gi/arg.h"
 #include "gi/function.h"
+#include "gi/gtype.h"
 #include "gjs/jsapi-util.h"
 
 // The global entry point for any invocations of GDestroyNotify; look up the
@@ -110,6 +114,37 @@ static void gjs_g_argument_set_array_length(GITypeTag tag, GIArgument* arg,
 #endif
 }
 
+GJS_JSAPI_RETURN_CONVENTION
+static bool report_primitive_type_mismatch(JSContext* cx,
+                                           GjsArgumentCache* self,
+                                           JS::HandleValue value,
+                                           JSType expected) {
+    static const char* typenames[] = {"undefined", "object", "function",
+                                      "string",    "number", "boolean",
+                                      "null",      "symbol", "bigint"};
+    static_assert(G_N_ELEMENTS(typenames) == JSTYPE_LIMIT,
+                  "Must match the values in JSType");
+
+    gjs_throw(cx, "Expected type %s for argument '%s' but got type %s",
+              typenames[expected], self->arg_name,
+              JS::InformalValueTypeName(value));
+    return false;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool report_out_of_range(JSContext* cx, GjsArgumentCache* self,
+                                GITypeTag tag) {
+    gjs_throw(cx, "Argument %s: value is out of range for %s", self->arg_name,
+              g_type_tag_to_string(tag));
+    return false;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool report_invalid_null(JSContext* cx, GjsArgumentCache* self) {
+    gjs_throw(cx, "Argument %s may not be null", self->arg_name);
+    return false;
+}
+
 // Marshallers:
 //
 // Each argument, irrespective of the direction, is processed in three phases:
@@ -277,6 +312,151 @@ static bool gjs_marshal_caller_allocates_in(JSContext*, GjsArgumentCache* self,
     return true;
 }
 
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_null_in_in(JSContext*, GjsArgumentCache*,
+                                   GjsFunctionCallState*, GIArgument* arg,
+                                   JS::HandleValue) {
+    arg->v_pointer = nullptr;
+    return true;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_boolean_in_in(JSContext*, GjsArgumentCache*,
+                                      GjsFunctionCallState*, GIArgument* arg,
+                                      JS::HandleValue value) {
+    arg->v_boolean = JS::ToBoolean(value);
+    return true;
+}
+
+// Type tags are alternated, signed / unsigned
+static int32_t min_max_ints[5][2] = {{G_MININT8, G_MAXINT8},
+                                     {0, G_MAXUINT8},
+                                     {G_MININT16, G_MAXINT16},
+                                     {0, G_MAXUINT16},
+                                     {G_MININT32, G_MAXINT32}};
+
+GJS_USE
+static inline bool value_in_range(int32_t number, GITypeTag tag) {
+    return (number >= min_max_ints[tag - GI_TYPE_TAG_INT8][0] &&
+            number <= min_max_ints[tag - GI_TYPE_TAG_INT8][1]);
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_integer_in_in(JSContext* cx, GjsArgumentCache* self,
+                                      GjsFunctionCallState*, GIArgument* arg,
+                                      JS::HandleValue value) {
+    GITypeTag tag = self->contents.number.number_tag;
+
+    if (self->contents.number.is_unsigned) {
+        uint32_t number;
+        if (!JS::ToUint32(cx, value, &number))
+            return false;
+
+        if (!value_in_range(number, tag))
+            return report_out_of_range(cx, self, tag);
+
+        gjs_g_argument_set_array_length(tag, arg, number);
+    } else {
+        int32_t number;
+        if (!JS::ToInt32(cx, value, &number))
+            return false;
+
+        if (!value_in_range(number, tag))
+            return report_out_of_range(cx, self, tag);
+
+        gjs_g_argument_set_array_length(tag, arg, number);
+    }
+
+    return true;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_number_in_in(JSContext* cx, GjsArgumentCache* self,
+                                     GjsFunctionCallState*, GIArgument* arg,
+                                     JS::HandleValue value) {
+    double v;
+    if (!JS::ToNumber(cx, value, &v))
+        return false;
+
+    GITypeTag tag = self->contents.number.number_tag;
+    if (tag == GI_TYPE_TAG_DOUBLE) {
+        arg->v_double = v;
+    } else if (tag == GI_TYPE_TAG_FLOAT) {
+        if (v < -G_MAXFLOAT || v > G_MAXFLOAT)
+            return report_out_of_range(cx, self, GI_TYPE_TAG_FLOAT);
+        arg->v_float = v;
+    } else if (tag == GI_TYPE_TAG_INT64) {
+        if (v < G_MININT64 || v > G_MAXINT64)
+            return report_out_of_range(cx, self, GI_TYPE_TAG_INT64);
+        arg->v_int64 = v;
+    } else if (tag == GI_TYPE_TAG_UINT64) {
+        if (v < 0 || v > G_MAXUINT64)
+            return report_out_of_range(cx, self, GI_TYPE_TAG_UINT64);
+        arg->v_uint64 = v;
+    } else if (tag == GI_TYPE_TAG_UINT32) {
+        if (v < 0 || v > G_MAXUINT32)
+            return report_out_of_range(cx, self, GI_TYPE_TAG_UINT32);
+        arg->v_uint32 = v;
+    } else {
+        g_assert_not_reached();
+    }
+
+    return true;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_unichar_in_in(JSContext* cx, GjsArgumentCache* self,
+                                      GjsFunctionCallState*, GIArgument* arg,
+                                      JS::HandleValue value) {
+    if (!value.isString())
+        return report_primitive_type_mismatch(cx, self, value, JSTYPE_STRING);
+
+    return gjs_unichar_from_string(cx, value, &arg->v_uint32);
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_gtype_in_in(JSContext* cx, GjsArgumentCache* self,
+                                    GjsFunctionCallState*, GIArgument* arg,
+                                    JS::HandleValue value) {
+    if (value.isNull())
+        return report_invalid_null(cx, self);
+    if (!value.isObject())
+        return report_primitive_type_mismatch(cx, self, value, JSTYPE_OBJECT);
+
+    JS::RootedObject gtype_obj(cx, &value.toObject());
+    return gjs_gtype_get_actual_gtype(cx, gtype_obj, &arg->v_ulong);
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_string_in_in(JSContext* cx, GjsArgumentCache* self,
+                                     GjsFunctionCallState*, 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.isString())
+        return report_primitive_type_mismatch(cx, self, value, JSTYPE_STRING);
+
+    if (self->contents.string_is_filename) {
+        GjsAutoChar str;
+        if (!gjs_string_to_filename(cx, value, &str))
+            return false;
+        arg->v_pointer = str.release();
+        return true;
+    }
+
+    JS::UniqueChars str = gjs_string_to_utf8(cx, value);
+    if (!str)
+        return false;
+    arg->v_pointer = g_strdup(str.get());
+    return true;
+}
+
 GJS_JSAPI_RETURN_CONVENTION
 static bool gjs_marshal_skipped_out(JSContext*, GjsArgumentCache*,
                                     GjsFunctionCallState*, GIArgument*,
@@ -437,6 +617,15 @@ static bool gjs_marshal_callback_release(JSContext*, GjsArgumentCache*,
     return true;
 }
 
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_marshal_string_in_release(JSContext*, GjsArgumentCache*,
+                                          GjsFunctionCallState*,
+                                          GIArgument* in_arg,
+                                          GIArgument* out_arg G_GNUC_UNUSED) {
+    g_free(in_arg->v_pointer);
+    return true;
+}
+
 static inline void gjs_arg_cache_set_skip_all(GjsArgumentCache* self) {
     self->marshal_in = gjs_marshal_skipped_in;
     self->marshal_out = gjs_marshal_skipped_out;
@@ -500,6 +689,93 @@ bool gjs_arg_cache_build_return(JSContext*, GjsArgumentCache* self,
     return true;
 }
 
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_arg_cache_build_normal_in_arg(JSContext*, GjsArgumentCache* self,
+                                              GITypeTag tag) {
+    // "Normal" in arguments are those arguments that don't require special
+    // processing, and don't touch other arguments.
+    // Main categories are:
+    // - void*
+    // - small numbers (fit in 32bit)
+    // - big numbers (need a double)
+    // - strings
+    // - enums/flags (different from numbers in the way they're exposed in GI)
+    // - objects (GObjects, boxed, unions, etc.)
+    // - hashes
+    // - sequences (null-terminated arrays, lists, etc.)
+
+    self->release = gjs_marshal_skipped_release;
+
+    switch (tag) {
+        case GI_TYPE_TAG_VOID:
+            self->marshal_in = gjs_marshal_null_in_in;
+            break;
+
+        case GI_TYPE_TAG_BOOLEAN:
+            self->marshal_in = gjs_marshal_boolean_in_in;
+            break;
+
+        case GI_TYPE_TAG_INT8:
+        case GI_TYPE_TAG_INT16:
+        case GI_TYPE_TAG_INT32:
+            self->marshal_in = gjs_marshal_integer_in_in;
+            self->contents.number.number_tag = tag;
+            self->contents.number.is_unsigned = false;
+            break;
+
+        case GI_TYPE_TAG_UINT8:
+        case GI_TYPE_TAG_UINT16:
+            self->marshal_in = gjs_marshal_integer_in_in;
+            self->contents.number.number_tag = tag;
+            self->contents.number.is_unsigned = true;
+            break;
+
+        case GI_TYPE_TAG_UINT32:
+        case GI_TYPE_TAG_INT64:
+        case GI_TYPE_TAG_UINT64:
+        case GI_TYPE_TAG_FLOAT:
+        case GI_TYPE_TAG_DOUBLE:
+            self->marshal_in = gjs_marshal_number_in_in;
+            self->contents.number.number_tag = tag;
+            break;
+
+        case GI_TYPE_TAG_UNICHAR:
+            self->marshal_in = gjs_marshal_unichar_in_in;
+            break;
+
+        case GI_TYPE_TAG_GTYPE:
+            self->marshal_in = gjs_marshal_gtype_in_in;
+            break;
+
+        case GI_TYPE_TAG_FILENAME:
+            self->marshal_in = gjs_marshal_string_in_in;
+            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)
+                self->release = gjs_marshal_string_in_release;
+            self->contents.string_is_filename = false;
+            break;
+
+        case GI_TYPE_TAG_INTERFACE:
+        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
+            self->marshal_in = gjs_marshal_generic_in_in;
+            self->release = gjs_marshal_generic_in_release;
+    }
+
+    return true;
+}
+
 bool gjs_arg_cache_build_arg(JSContext* cx, GjsArgumentCache* self,
                              GjsArgumentCache* arguments, int gi_index,
                              GIDirection direction, GIArgInfo* arg,
@@ -650,10 +926,12 @@ bool gjs_arg_cache_build_arg(JSContext* cx, GjsArgumentCache* self,
     }
 
     if (direction == GI_DIRECTION_IN) {
-        self->marshal_in = gjs_marshal_generic_in_in;
+        bool ok = gjs_arg_cache_build_normal_in_arg(cx, self, type_tag);
         self->marshal_out = gjs_marshal_skipped_out;
-        self->release = gjs_marshal_generic_in_release;
-    } else if (direction == GI_DIRECTION_INOUT) {
+        return ok;
+    }
+
+    if (direction == GI_DIRECTION_INOUT) {
         self->marshal_in = gjs_marshal_generic_inout_in;
         self->marshal_out = gjs_marshal_generic_out_out;
         self->release = gjs_marshal_generic_inout_release;
diff --git a/gi/arg-cache.h b/gi/arg-cache.h
index a7591204..fc917cf1 100644
--- a/gi/arg-cache.h
+++ b/gi/arg-cache.h
@@ -71,6 +71,14 @@ typedef struct _GjsArgumentCache {
             int destroy_pos;
         } callback;
 
+        struct {
+            GITypeTag number_tag;
+            bool is_unsigned : 1;
+        } number;
+
+        // string / filename
+        bool string_is_filename : 1;
+
         // out caller allocates (FIXME: should be in object)
         size_t caller_allocates_size;
     } contents;


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