[gjs/ewlsh/fix-function-pointers] Some really rough work on fixing function pointer support




commit 8c585c9706b19c213682af9319789642cdcb15c6
Author: Evan Welsh <contact evanwelsh com>
Date:   Fri Aug 6 22:27:14 2021 -0700

    Some really rough work on fixing function pointer support
    
    Not sure if we should go this route or wrap in a callback, I'd prefer
    if we can go this route to avoid adding JS wrapping

 gi/arg-cache.cpp |  21 +++++-
 gi/arg-cache.h   |   4 +
 gi/function.cpp  | 225 ++++++++++++++++++++++++++++++++++---------------------
 gi/function.h    |   3 +
 log.js           |   5 ++
 5 files changed, 170 insertions(+), 88 deletions(-)
---
diff --git a/gi/arg-cache.cpp b/gi/arg-cache.cpp
index dd46b88a..2499fbe5 100644
--- a/gi/arg-cache.cpp
+++ b/gi/arg-cache.cpp
@@ -284,9 +284,24 @@ static bool gjs_marshal_callback_in(JSContext* cx, GjsArgumentCache* self,
             return false;
         }
 
-        JS::RootedFunction func(cx, JS_GetObjectFunction(&value.toObject()));
+        JS::RootedObject obj(cx, &value.toObject());
+        JS::RootedFunction func(cx, JS_GetObjectFunction(obj));
+
         GjsAutoCallableInfo callable_info =
             g_type_info_get_interface(&self->type_info);
+
+        if (!func && gjs_is_function(cx, obj)) {
+            GCallback callback = nullptr;
+            if (!gjs_function_to_callback(cx, obj, callable_info, &callback))
+                return false;
+            gjs_arg_set(arg, callback);
+            // Prevent GJS from attempting to cleanup this argument...
+            self->contents.callback.raw_pointer = true;
+            return true;
+        } else {
+            self->contents.callback.raw_pointer = false;
+        }
+
         bool is_object_method = !!state->instance_object;
         trampoline = GjsCallbackTrampoline::create(
             cx, func, callable_info, self->contents.callback.scope,
@@ -925,12 +940,12 @@ static bool gjs_marshal_caller_allocates_release(JSContext*, GjsArgumentCache*,
 }
 
 GJS_JSAPI_RETURN_CONVENTION
-static bool gjs_marshal_callback_release(JSContext*, GjsArgumentCache*,
+static bool gjs_marshal_callback_release(JSContext*, GjsArgumentCache* cache,
                                          GjsFunctionCallState*,
                                          GIArgument* in_arg,
                                          GIArgument* out_arg [[maybe_unused]]) {
     auto* closure = gjs_arg_get<ffi_closure*>(in_arg);
-    if (!closure)
+    if (!closure || !!cache->contents.callback.raw_pointer)
         return true;
 
     g_closure_unref(static_cast<GClosure*>(closure->user_data));
diff --git a/gi/arg-cache.h b/gi/arg-cache.h
index 562dcd3d..912135c9 100644
--- a/gi/arg-cache.h
+++ b/gi/arg-cache.h
@@ -69,6 +69,7 @@ struct GjsArgumentCache {
             uint8_t closure_pos;
             uint8_t destroy_pos;
             GIScopeType scope : 2;
+            bool raw_pointer : 1;
         } callback;
 
         struct {
@@ -117,6 +118,9 @@ struct GjsArgumentCache {
         g_assert(pos <= MAX_ARGS && "No more than 253 arguments allowed");
         contents.array.length_pos = pos;
     }
+    [[nodiscard]] bool has_raw_pointer() {
+        return contents.callback.raw_pointer;
+    }
     void set_callback_destroy_pos(int pos) {
         g_assert(pos <= MAX_ARGS && "No more than 253 arguments allowed");
         contents.callback.destroy_pos = pos < 0 ? ABSENT : pos;
diff --git a/gi/function.cpp b/gi/function.cpp
index 4c9c649a..669e8981 100644
--- a/gi/function.cpp
+++ b/gi/function.cpp
@@ -29,8 +29,8 @@
 #include <js/Value.h>
 #include <js/ValueArray.h>
 #include <js/Warnings.h>
-#include <jsapi.h>        // for HandleValueArray, JS_GetElement
-#include <jspubtd.h>      // for JSProtoKey
+#include <jsapi.h>    // for HandleValueArray, JS_GetElement
+#include <jspubtd.h>  // for JSProtoKey
 
 #include "gi/arg-cache.h"
 #include "gi/arg-inl.h"
@@ -150,6 +150,8 @@ class Function : public CWrapper<Function> {
         &Function::class_spec};
 
  public:
+    GCallback to_callback() { return FFI_FN(m_invoker.native_address); }
+
     GJS_JSAPI_RETURN_CONVENTION
     static JSObject* create(JSContext* cx, GType gtype, GICallableInfo* info);
 
@@ -194,44 +196,41 @@ static inline std::enable_if_t<std::is_pointer_v<T>> set_ffi_arg(
         gjs_pointer_to_int<ffi_arg>(gjs_arg_get<T, TAG>(value));
 }
 
-static void
-set_return_ffi_arg_from_giargument (GITypeInfo  *ret_type,
-                                    void        *result,
-                                    GIArgument  *return_value)
-{
+static void set_return_ffi_arg_from_giargument(GITypeInfo* ret_type,
+                                               void* result,
+                                               GIArgument* return_value) {
     // Be consistent with gjs_value_to_g_argument()
     switch (g_type_info_get_tag(ret_type)) {
-    case GI_TYPE_TAG_VOID:
-        g_assert_not_reached();
-    case GI_TYPE_TAG_INT8:
-        set_ffi_arg<int8_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_UINT8:
-        set_ffi_arg<uint8_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_INT16:
-        set_ffi_arg<int16_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_UINT16:
-        set_ffi_arg<uint16_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_INT32:
-        set_ffi_arg<int32_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_UINT32:
-        set_ffi_arg<uint32_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_BOOLEAN:
-        set_ffi_arg<gboolean, GI_TYPE_TAG_BOOLEAN>(result, return_value);
-        break;
-    case GI_TYPE_TAG_UNICHAR:
-        set_ffi_arg<char32_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_INT64:
-        set_ffi_arg<int64_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_INTERFACE:
-        {
+        case GI_TYPE_TAG_VOID:
+            g_assert_not_reached();
+        case GI_TYPE_TAG_INT8:
+            set_ffi_arg<int8_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_UINT8:
+            set_ffi_arg<uint8_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_INT16:
+            set_ffi_arg<int16_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_UINT16:
+            set_ffi_arg<uint16_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_INT32:
+            set_ffi_arg<int32_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_UINT32:
+            set_ffi_arg<uint32_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_BOOLEAN:
+            set_ffi_arg<gboolean, GI_TYPE_TAG_BOOLEAN>(result, return_value);
+            break;
+        case GI_TYPE_TAG_UNICHAR:
+            set_ffi_arg<char32_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_INT64:
+            set_ffi_arg<int64_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_INTERFACE: {
             GIInfoType interface_type;
 
             GjsAutoBaseInfo interface_info(g_type_info_get_interface(ret_type));
@@ -242,41 +241,42 @@ set_return_ffi_arg_from_giargument (GITypeInfo  *ret_type,
                 set_ffi_arg<int, GI_TYPE_TAG_INTERFACE>(result, return_value);
             else
                 set_ffi_arg<void*>(result, return_value);
-        }
-        break;
-    case GI_TYPE_TAG_UINT64:
-        // Other primitive types need to squeeze into 64-bit ffi_arg too
-        set_ffi_arg<uint64_t>(result, return_value);
-        break;
-    case GI_TYPE_TAG_FLOAT:
-        set_ffi_arg<float>(result, return_value);
-        break;
-    case GI_TYPE_TAG_DOUBLE:
-        set_ffi_arg<double>(result, return_value);
-        break;
-    case GI_TYPE_TAG_GTYPE:
-        set_ffi_arg<GType, GI_TYPE_TAG_GTYPE>(result, return_value);
-        break;
-    case GI_TYPE_TAG_UTF8:
-    case GI_TYPE_TAG_FILENAME:
-        set_ffi_arg<char*>(result, return_value);
-        break;
-    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:
-        set_ffi_arg<void*>(result, return_value);
-        break;
+        } break;
+        case GI_TYPE_TAG_UINT64:
+            // Other primitive types need to squeeze into 64-bit ffi_arg too
+            set_ffi_arg<uint64_t>(result, return_value);
+            break;
+        case GI_TYPE_TAG_FLOAT:
+            set_ffi_arg<float>(result, return_value);
+            break;
+        case GI_TYPE_TAG_DOUBLE:
+            set_ffi_arg<double>(result, return_value);
+            break;
+        case GI_TYPE_TAG_GTYPE:
+            set_ffi_arg<GType, GI_TYPE_TAG_GTYPE>(result, return_value);
+            break;
+        case GI_TYPE_TAG_UTF8:
+        case GI_TYPE_TAG_FILENAME:
+            set_ffi_arg<char*>(result, return_value);
+            break;
+        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:
+            set_ffi_arg<void*>(result, return_value);
+            break;
     }
 }
 
 void GjsCallbackTrampoline::warn_about_illegal_js_callback(const char* when,
                                                            const char* reason) {
-    g_critical("Attempting to run a JS callback %s. This is most likely caused "
-               "by %s. Because it would crash the application, it has been "
-               "blocked.", when, reason);
+    g_critical(
+        "Attempting to run a JS callback %s. This is most likely caused "
+        "by %s. Because it would crash the application, it has been "
+        "blocked.",
+        when, reason);
     if (m_info)
         g_critical("The offending callback was %s()%s.", m_info.name(),
                    m_is_vfunc ? ", a vfunc" : "");
@@ -459,7 +459,8 @@ bool GjsCallbackTrampoline::callback_closure_inner(
             case PARAM_SKIPPED:
                 continue;
             case PARAM_ARRAY: {
-                gint array_length_pos = g_type_info_get_array_length(&type_info);
+                gint array_length_pos =
+                    g_type_info_get_array_length(&type_info);
                 GIArgInfo array_length_arg;
                 GITypeInfo arg_type_info;
 
@@ -686,7 +687,8 @@ bool GjsCallbackTrampoline::initialize() {
 
                     g_callable_info_load_arg(m_info, array_length_pos,
                                              &length_arg_info);
-                    if (g_arg_info_get_direction(&length_arg_info) != direction) {
+                    if (g_arg_info_get_direction(&length_arg_info) !=
+                        direction) {
                         gjs_throw(context(),
                                   "%s %s has an array with different-direction "
                                   "length argument. This is not supported",
@@ -924,8 +926,8 @@ bool Function::invoke(JSContext* context, const JS::CallArgs& args,
     // This pointer needs to exist on the stack across the ffi_call() call
     GError** errorp = state.local_error.out();
 
-    /* Did argument conversion fail?  In that case, skip invocation and jump to release
-     * processing. */
+    /* Did argument conversion fail?  In that case, skip invocation and jump to
+     * release processing. */
     if (state.failed)
         return finish_invoke(context, args, &state, r_value);
 
@@ -1118,7 +1120,8 @@ Function::~Function() {
 
 void Function::finalize_impl(JSFreeOp*, Function* priv) {
     if (priv == NULL)
-        return; /* we are the prototype, not a real instance, so constructor never called */
+        return; /* we are the prototype, not a real instance, so constructor
+                   never called */
     delete priv;
 }
 
@@ -1203,7 +1206,7 @@ const JSFunctionSpec Function::proto_funcs[] = {
 
 bool Function::init(JSContext* context, GType gtype /* = G_TYPE_NONE */) {
     guint8 i;
-    GError *error = NULL;
+    GError* error = NULL;
 
     if (m_info.type() == GI_INFO_TYPE_FUNCTION) {
         if (!g_function_info_prep_invoker(m_info, &m_invoker, &error))
@@ -1306,17 +1309,13 @@ JSObject* Function::create(JSContext* context, GType gtype,
 }  // namespace Gjs
 
 GJS_JSAPI_RETURN_CONVENTION
-JSObject*
-gjs_define_function(JSContext       *context,
-                    JS::HandleObject in_object,
-                    GType            gtype,
-                    GICallableInfo  *info)
-{
+JSObject* gjs_define_function(JSContext* context, JS::HandleObject in_object,
+                              GType gtype, GICallableInfo* info) {
     GIInfoType info_type;
-    gchar *name;
+    gchar* name;
     bool free_name;
 
-    info_type = g_base_info_get_type((GIBaseInfo *)info);
+    info_type = g_base_info_get_type((GIBaseInfo*)info);
 
     JS::RootedObject function(context,
                               Gjs::Function::create(context, gtype, info));
@@ -1324,13 +1323,14 @@ gjs_define_function(JSContext       *context,
         return NULL;
 
     if (info_type == GI_INFO_TYPE_FUNCTION) {
-        name = (gchar *) g_base_info_get_name((GIBaseInfo*) info);
+        name = (gchar*)g_base_info_get_name((GIBaseInfo*)info);
         free_name = false;
     } else if (info_type == GI_INFO_TYPE_VFUNC) {
-        name = g_strdup_printf("vfunc_%s", g_base_info_get_name((GIBaseInfo*) info));
+        name = g_strdup_printf("vfunc_%s",
+                               g_base_info_get_name((GIBaseInfo*)info));
         free_name = true;
     } else {
-        g_assert_not_reached ();
+        g_assert_not_reached();
     }
 
     if (!JS_DefineProperty(context, in_object, name, function,
@@ -1345,6 +1345,61 @@ gjs_define_function(JSContext       *context,
     return function;
 }
 
+bool gjs_is_function(JSContext* cx, JS::HandleObject function_object) {
+    return Gjs::Function::typecheck(cx, function_object, nullptr);
+}
+
+bool gjs_function_to_callback(JSContext* cx, JS::HandleObject function_object,
+                              GICallbackInfo* info, GCallback* func_pointer) {
+    Gjs::Function* func;
+
+    if (!Gjs::Function::for_js_typecheck(cx, function_object, &func)) {
+        return false;
+    }
+
+    // auto function_info = func->m_info;
+    // auto function_args_count = g_callable_info_get_n_args(function_info);
+    // auto args_count = g_callable_info_get_n_args(info);
+
+    // if (function_args_count != = args_count) {
+    //     gjs_throw(cx, "Function does not have %i arguments.", args_count);
+    //     return false;
+    // }
+
+    // for (auto i = 0; i < args_count; i++) {
+    //     GIArgInfo function_arg;
+    //     GIArgInfo callback_arg;
+    //     g_callable_info_load_arg(function_info, i, &function_arg);
+    //     g_callable_info_load_arg(info, i, &callback_arg);
+
+    //     GITypeInfo function_arg_type;
+    //     g_arg_info_load_type(function_arg, &function_arg_type);
+    //     GITypeInfo callback_arg_type;
+    //     g_arg_info_load_type(callback_arg,
+    //                                                        &callback_arg_type);
+    //     if (!GI_IS_REGISTERED_TYPE_INFO(&function_arg_type) ||
+    //         !GI_IS_REGISTERED_TYPE_INFO(&callback_arg_type)) {
+    //         gjs_throw(cx, "Callback or function has non-registered type.");
+    //         return false;
+    //     }
+    //     GIRegisteredTypeInfo* rf_arg_type = &function_arg_type;
+    //     GIRegisteredTypeInfo* rc_arg_type = &callback_arg_type;
+
+    //     GType f_type = g_registered_type_info_get_g_type(rf_arg_type);
+    //     GType c_type = g_registered_type_info_get_g_type(rc_arg_type);
+
+    //     if (!g_type_is_a(f_type, c_type)) {
+    //         gjs_throw(cx, "Callback argument %i expected %s but received
+    //         %s.",
+    //                   i, g_type_name(c_type), g_type_name(f_type));
+    //         return false;
+    //     }
+    // }
+
+    *func_pointer = func->to_callback();
+    return true;
+}
+
 bool gjs_invoke_constructor_from_c(JSContext* context, GIFunctionInfo* info,
                                    JS::HandleObject obj,
                                    const JS::CallArgs& args,
diff --git a/gi/function.h b/gi/function.h
index 498b5481..c173b316 100644
--- a/gi/function.h
+++ b/gi/function.h
@@ -157,4 +157,7 @@ bool gjs_invoke_constructor_from_c(JSContext* cx, GIFunctionInfo* info,
                                    const JS::CallArgs& args,
                                    GIArgument* rvalue);
 
+bool gjs_is_function(JSContext* cx, JS::HandleObject function_object);
+bool gjs_function_to_callback(JSContext* cx, JS::HandleObject function_object,
+                              GICallbackInfo* info, GCallback* func_pointer);
 #endif  // GI_FUNCTION_H_
diff --git a/log.js b/log.js
new file mode 100644
index 00000000..78b602c3
--- /dev/null
+++ b/log.js
@@ -0,0 +1,5 @@
+const {GLib} =imports.gi;
+
+GLib.log_set_writer_func(GLib.log_writer_default);
+
+GLib.log_structured('Gjs-Console-Test', GLib.LogLevelFlags.LEVEL_MESSAGE, { 'MESSAGE': 'TESTING'});
\ No newline at end of file


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