[gjs: 1/9] wrapperutils: Factor out gjs_define_static_methods()



commit f7153d4df12f02ed08c4149a11c8f08566c704a1
Author: Philip Chimento <philip chimento gmail com>
Date:   Sun Dec 30 16:31:17 2018 -0700

    wrapperutils: Factor out gjs_define_static_methods()
    
    The same code is repeated several times in a few different functions,
    because of there being different APIs in gobject-introspection for
    g_struct_info_get_method(), g_object_info_get_method(),
    g_interface_info_get_method(), etc. These functions had diverged
    slightly. For example, the GIInterfaceInfo version did not define static
    methods from the interface's gtype struct, while the GIObjectInfo
    version did.
    
    We can consolidate these, in order to prevent future divergence, using a
    policy template to provide the polymorphism.
    
    Unfortunately the template parameter cannot be the type of
    the introspection info (GIStructInfo, GIObjectInfo, etc.) because these
    are all typedefs for GIBaseInfo and therefore cannot be used to
    differentiate the templates. Neither can the GIInfoType tag be used as
    the template parameter, because GI_INFO_TYPE_BOXED can be either a
    GIStructInfo or GIUnionInfo. It's also not possible to just cheat and
    use the same API for each one, because the n_methods fields are all at
    different offsets in the different introspection info records.
    
    Therefore, we introduce InfoType::Struct, InfoType::Object, etc., as the
    template parameters for gjs_define_static_methods().
    
    As a result of this change, interfaces now get static methods from the
    interface's gtype struct.

 gi/boxed.cpp        | 37 ++-----------------------
 gi/enumeration.cpp  | 36 ++----------------------
 gi/enumeration.h    |  5 ----
 gi/fundamental.cpp  |  4 +--
 gi/gerror.cpp       |  3 +-
 gi/interface.cpp    | 37 ++-----------------------
 gi/object.cpp       | 52 ++--------------------------------
 gi/object.h         |  6 ----
 gi/param.cpp        |  3 +-
 gi/wrapperutils.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gi/wrapperutils.h   | 14 ++++++++++
 11 files changed, 108 insertions(+), 169 deletions(-)
---
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index 8338c8e5..2ede0534 100644
--- a/gi/boxed.cpp
+++ b/gi/boxed.cpp
@@ -79,39 +79,6 @@ extern struct JSClass gjs_boxed_class;
 
 GJS_DEFINE_PRIV_FROM_JS(Boxed, gjs_boxed_class)
 
-GJS_JSAPI_RETURN_CONVENTION
-static bool
-gjs_define_static_methods(JSContext       *context,
-                          JS::HandleObject constructor,
-                          GType            gtype,
-                          GIStructInfo    *boxed_info)
-{
-    int i;
-    int n_methods;
-
-    n_methods = g_struct_info_get_n_methods(boxed_info);
-
-    for (i = 0; i < n_methods; i++) {
-        GIFunctionInfoFlags flags;
-
-        GjsAutoFunctionInfo meth_info = g_struct_info_get_method(boxed_info, i);
-        flags = g_function_info_get_flags (meth_info);
-
-        /* Anything that isn't a method we put on the prototype of the
-         * constructor.  This includes <constructor> introspection
-         * methods, as well as the forthcoming "static methods"
-         * support.  We may want to change this to use
-         * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the
-         * like in the near future.
-         */
-        if (!(flags & GI_FUNCTION_IS_METHOD)) {
-            if (!gjs_define_function(context, constructor, gtype, meth_info))
-                return false;
-        }
-    }
-    return true;
-}
-
 /* The *resolved out parameter, on success, should be false to indicate that id
  * was not resolved; and true if id was resolved. */
 GJS_JSAPI_RETURN_CONVENTION
@@ -1168,8 +1135,8 @@ bool gjs_define_boxed_class(JSContext* context, JS::HandleObject in_object,
     priv->can_allocate_directly = struct_is_simple (priv->info);
 
     if (!define_boxed_class_fields(context, priv, prototype) ||
-        !gjs_define_static_methods(context, constructor, priv->gtype,
-                                   priv->info))
+        !gjs_define_static_methods<InfoType::Struct>(context, constructor,
+                                                     priv->gtype, priv->info))
         return false;
 
     if (priv->gtype == G_TYPE_ERROR &&
diff --git a/gi/enumeration.cpp b/gi/enumeration.cpp
index 9b19b771..78c77c0c 100644
--- a/gi/enumeration.cpp
+++ b/gi/enumeration.cpp
@@ -107,39 +107,6 @@ gjs_define_enum_values(JSContext       *context,
     return true;
 }
 
-bool
-gjs_define_enum_static_methods(JSContext       *context,
-                               JS::HandleObject constructor,
-                               GIEnumInfo      *enum_info)
-{
-    int i, n_methods;
-
-    n_methods = g_enum_info_get_n_methods(enum_info);
-
-    for (i = 0; i < n_methods; i++) {
-        GIFunctionInfoFlags flags;
-
-        GjsAutoFunctionInfo meth_info = g_enum_info_get_method(enum_info, i);
-        flags = g_function_info_get_flags(meth_info);
-
-        g_warn_if_fail(!(flags & GI_FUNCTION_IS_METHOD));
-        /* Anything that isn't a method we put on the prototype of the
-         * constructor.  This includes <constructor> introspection
-         * methods, as well as the forthcoming "static methods"
-         * support.  We may want to change this to use
-         * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the
-         * like in the near future.
-         */
-        if (!(flags & GI_FUNCTION_IS_METHOD)) {
-            if (!gjs_define_function(context, constructor, G_TYPE_NONE,
-                                     meth_info))
-                return false;
-        }
-    }
-
-    return true;
-}
-
 bool
 gjs_define_enumeration(JSContext       *context,
                        JS::HandleObject in_object,
@@ -168,7 +135,8 @@ gjs_define_enumeration(JSContext       *context,
     GType gtype = g_registered_type_info_get_g_type(info);
 
     if (!gjs_define_enum_values(context, enum_obj, info) ||
-        !gjs_define_enum_static_methods(context, enum_obj, info) ||
+        !gjs_define_static_methods<InfoType::Enum>(context, enum_obj, gtype,
+                                                   info) ||
         !gjs_wrapper_define_gtype_prop(context, enum_obj, gtype))
         return false;
 
diff --git a/gi/enumeration.h b/gi/enumeration.h
index ff804e33..7a81c027 100644
--- a/gi/enumeration.h
+++ b/gi/enumeration.h
@@ -39,11 +39,6 @@ bool gjs_define_enum_values(JSContext       *context,
                             JS::HandleObject in_object,
                             GIEnumInfo      *info);
 
-GJS_JSAPI_RETURN_CONVENTION
-bool gjs_define_enum_static_methods(JSContext       *context,
-                                    JS::HandleObject constructor,
-                                    GIEnumInfo      *enum_info);
-
 GJS_JSAPI_RETURN_CONVENTION
 bool gjs_define_enumeration(JSContext       *context,
                             JS::HandleObject in_object,
diff --git a/gi/fundamental.cpp b/gi/fundamental.cpp
index ee25a604..433607ab 100644
--- a/gi/fundamental.cpp
+++ b/gi/fundamental.cpp
@@ -704,8 +704,8 @@ gjs_define_fundamental_class(JSContext              *context,
                   g_base_info_get_name ((GIBaseInfo *)priv->info));
     }
 
-    return gjs_object_define_static_methods(context, constructor, gtype,
-                                            info) &&
+    return gjs_define_static_methods<InfoType::Object>(context, constructor,
+                                                       gtype, info) &&
            gjs_wrapper_define_gtype_prop(context, constructor, gtype);
 }
 
diff --git a/gi/gerror.cpp b/gi/gerror.cpp
index 485af08e..f7c28ed4 100644
--- a/gi/gerror.cpp
+++ b/gi/gerror.cpp
@@ -347,7 +347,8 @@ bool gjs_define_error_class(JSContext* context, JS::HandleObject in_object,
               in_object.get());
 
     return gjs_define_enum_values(context, constructor, priv->info) &&
-           gjs_define_enum_static_methods(context, constructor, priv->info);
+           gjs_define_static_methods<InfoType::Enum>(context, constructor,
+                                                     G_TYPE_ERROR, priv->info);
 }
 
 GJS_USE
diff --git a/gi/interface.cpp b/gi/interface.cpp
index 4618a7b1..c6c8b6ed 100644
--- a/gi/interface.cpp
+++ b/gi/interface.cpp
@@ -71,39 +71,6 @@ interface_finalize(JSFreeOp *fop,
     g_slice_free(Interface, priv);
 }
 
-GJS_JSAPI_RETURN_CONVENTION
-static bool
-gjs_define_static_methods(JSContext       *context,
-                          JS::HandleObject constructor,
-                          GType            gtype,
-                          GIInterfaceInfo *info)
-{
-    int i;
-    int n_methods;
-
-    n_methods = g_interface_info_get_n_methods(info);
-
-    for (i = 0; i < n_methods; i++) {
-        GIFunctionInfoFlags flags;
-
-        GjsAutoFunctionInfo meth_info = g_interface_info_get_method(info, i);
-        flags = g_function_info_get_flags (meth_info);
-
-        /* Anything that isn't a method we put on the prototype of the
-         * constructor.  This includes <constructor> introspection
-         * methods, as well as the forthcoming "static methods"
-         * support.  We may want to change this to use
-         * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the
-         * like in the near future.
-         */
-        if (!(flags & GI_FUNCTION_IS_METHOD)) {
-            if (!gjs_define_function(context, constructor, gtype, meth_info))
-                return false;
-        }
-    }
-    return true;
-}
-
 GJS_JSAPI_RETURN_CONVENTION
 static bool
 interface_resolve(JSContext       *context,
@@ -252,8 +219,8 @@ gjs_define_interface_class(JSContext              *context,
     /* If we have no GIRepository information, then this interface was defined
      * from within GJS and therefore has no C static methods to be defined. */
     if (priv->info) {
-        if (!gjs_define_static_methods(context, constructor, priv->gtype,
-                                       priv->info))
+        if (!gjs_define_static_methods<InfoType::Interface>(
+                context, constructor, priv->gtype, priv->info))
             return false;
     }
 
diff --git a/gi/object.cpp b/gi/object.cpp
index 35a083fd..e35f8aaf 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -2115,55 +2115,6 @@ JSFunctionSpec gjs_object_instance_proto_funcs[] = {
     JS_FN("toString", &ObjectBase::to_string, 0, 0),
     JS_FS_END};
 
-bool
-gjs_object_define_static_methods(JSContext       *context,
-                                 JS::HandleObject constructor,
-                                 GType            gtype,
-                                 GIObjectInfo    *object_info)
-{
-    int i;
-    int n_methods;
-
-    n_methods = g_object_info_get_n_methods(object_info);
-
-    for (i = 0; i < n_methods; i++) {
-        GIFunctionInfoFlags flags;
-
-        GjsAutoCallableInfo meth_info =
-            g_object_info_get_method(object_info, i);
-        flags = g_function_info_get_flags (meth_info);
-
-        /* Anything that isn't a method we put on the prototype of the
-         * constructor.  This includes <constructor> introspection
-         * methods, as well as the forthcoming "static methods"
-         * support.  We may want to change this to use
-         * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the
-         * like in the near future.
-         */
-        if (!(flags & GI_FUNCTION_IS_METHOD)) {
-            if (!gjs_define_function(context, constructor, gtype, meth_info))
-                return false;
-        }
-    }
-
-    GjsAutoStructInfo gtype_struct =
-        g_object_info_get_class_struct(object_info);
-    if (gtype_struct == NULL)
-        return true;  /* not an error? */
-
-    n_methods = g_struct_info_get_n_methods(gtype_struct);
-
-    for (i = 0; i < n_methods; i++) {
-        GjsAutoCallableInfo meth_info =
-            g_struct_info_get_method(gtype_struct, i);
-
-        if (!gjs_define_function(context, constructor, gtype, meth_info))
-            return false;
-    }
-
-    return true;
-}
-
 bool
 gjs_define_object_class(JSContext              *context,
                         JS::HandleObject        in_object,
@@ -2263,7 +2214,8 @@ gjs_define_object_class(JSContext              *context,
               prototype.get(), JS_GetClass(prototype), in_object.get());
 
     if (info)
-        if (!gjs_object_define_static_methods(context, constructor, gtype, info))
+        if (!gjs_define_static_methods<InfoType::Object>(context, constructor,
+                                                         gtype, info))
             return false;
 
     return gjs_wrapper_define_gtype_prop(context, constructor, gtype);
diff --git a/gi/object.h b/gi/object.h
index 2977ec14..bf7932a3 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -567,12 +567,6 @@ void gjs_object_shutdown_toggle_queue(void);
 void gjs_object_context_dispose_notify(void    *data,
                                        GObject *where_the_object_was);
 
-GJS_JSAPI_RETURN_CONVENTION
-bool gjs_object_define_static_methods(JSContext       *context,
-                                      JS::HandleObject constructor,
-                                      GType            gtype,
-                                      GIObjectInfo    *object_info);
-
 G_END_DECLS
 
 #endif  /* __GJS_OBJECT_H__ */
diff --git a/gi/param.cpp b/gi/param.cpp
index 7195d5a5..7997f800 100644
--- a/gi/param.cpp
+++ b/gi/param.cpp
@@ -218,7 +218,8 @@ gjs_define_param_class(JSContext       *context,
         return false;
 
     GjsAutoObjectInfo info = g_irepository_find_by_gtype(nullptr, G_TYPE_PARAM);
-    if (!gjs_object_define_static_methods(context, constructor, G_TYPE_PARAM, info))
+    if (!gjs_define_static_methods<InfoType::Object>(context, constructor,
+                                                     G_TYPE_PARAM, info))
         return false;
 
     gjs_debug(GJS_DEBUG_GPARAM, "Defined class %s prototype is %p class %p in object %p",
diff --git a/gi/wrapperutils.cpp b/gi/wrapperutils.cpp
index 3814c40a..caa441e9 100644
--- a/gi/wrapperutils.cpp
+++ b/gi/wrapperutils.cpp
@@ -25,6 +25,7 @@
 
 #include <string.h>
 
+#include "gi/function.h"
 #include "gi/wrapperutils.h"
 #include "gjs/context-private.h"
 
@@ -94,3 +95,82 @@ bool gjs_wrapper_define_gtype_prop(JSContext* cx, JS::HandleObject constructor,
     return JS_DefinePropertyById(cx, constructor, atoms.gtype(), gtype_obj,
                                  GJS_MODULE_PROP_FLAGS);
 }
+
+// These policies work around having separate g_foo_info_get_n_methods() and
+// g_foo_info_get_method() functions for different GIInfoTypes. It's not
+// possible to use GIFooInfo* as the template parameter, because the GIFooInfo
+// structs are all typedefs of GIBaseInfo. It's also not possible to use the
+// GIInfoType enum value as the template parameter, because GI_INFO_TYPE_BOXED
+// could be either a GIStructInfo or GIUnionInfo.
+template <InfoType::Tag TAG>
+struct InfoMethodsPolicy {};
+
+#define DECLARE_POLICY(tag, type, type_struct_func)                            \
+    template <>                                                                \
+    struct InfoMethodsPolicy<InfoType::tag> {                                  \
+        using T = GI##tag##Info;                                               \
+        static constexpr int (*n_methods)(T*) = g_##type##_info_get_n_methods; \
+        static constexpr GIFunctionInfo* (*method)(T*, int) = g_##type         \
+            ##_info_get_method;                                                \
+        static constexpr GIStructInfo* (*type_struct)(T*) = type_struct_func;  \
+    };
+
+DECLARE_POLICY(Enum, enum, nullptr)
+DECLARE_POLICY(Interface, interface, g_interface_info_get_iface_struct)
+DECLARE_POLICY(Object, object, g_object_info_get_class_struct)
+DECLARE_POLICY(Struct, struct, nullptr)
+
+#undef DECLARE_POLICY
+
+template <InfoType::Tag TAG>
+bool gjs_define_static_methods(JSContext* cx, JS::HandleObject constructor,
+                               GType gtype, GIBaseInfo* info) {
+    int n_methods = InfoMethodsPolicy<TAG>::n_methods(info);
+
+    for (int ix = 0; ix < n_methods; ix++) {
+        GjsAutoFunctionInfo meth_info =
+            InfoMethodsPolicy<TAG>::method(info, ix);
+        GIFunctionInfoFlags flags = g_function_info_get_flags(meth_info);
+
+        // Anything that isn't a method we put on the constructor. This
+        // includes <constructor> introspection methods, as well as static
+        // methods. We may want to change this to use
+        // GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the like
+        // in the future.
+        if (!(flags & GI_FUNCTION_IS_METHOD)) {
+            if (!gjs_define_function(cx, constructor, gtype, meth_info))
+                return false;
+        }
+    }
+
+    if (!InfoMethodsPolicy<TAG>::type_struct)
+        return true;
+
+    // Also define class/interface methods if there is a gtype struct
+
+    GjsAutoStructInfo type_struct = InfoMethodsPolicy<TAG>::type_struct(info);
+    if (!type_struct)
+        return true;  // not an error?
+
+    n_methods = g_struct_info_get_n_methods(type_struct);
+
+    for (int ix = 0; ix < n_methods; ix++) {
+        GjsAutoFunctionInfo meth_info =
+            g_struct_info_get_method(type_struct, ix);
+
+        if (!gjs_define_function(cx, constructor, gtype, meth_info))
+            return false;
+    }
+
+    return true;
+}
+
+// All possible instantiations are needed
+template bool gjs_define_static_methods<InfoType::Enum>(
+    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
+template bool gjs_define_static_methods<InfoType::Interface>(
+    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
+template bool gjs_define_static_methods<InfoType::Object>(
+    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
+template bool gjs_define_static_methods<InfoType::Struct>(
+    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
diff --git a/gi/wrapperutils.h b/gi/wrapperutils.h
index 321db128..b121a647 100644
--- a/gi/wrapperutils.h
+++ b/gi/wrapperutils.h
@@ -48,4 +48,18 @@ bool gjs_wrapper_define_gtype_prop(JSContext* cx, JS::HandleObject constructor,
 
 G_END_DECLS
 
+namespace InfoType {
+enum Tag { Enum, Interface, Object, Struct };
+};
+
+/*
+ * gjs_define_static_methods:
+ *
+ * Defines all static methods from @info on @constructor. Also includes class
+ * methods for GIObjectInfo, and interface methods for GIInterfaceInfo.
+ */
+template <InfoType::Tag>
+GJS_JSAPI_RETURN_CONVENTION bool gjs_define_static_methods(
+    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
+
 #endif  // GI_WRAPPERUTILS_H_


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