diff options
author | Evan Welsh <contact@evanwelsh.com> | 2021-08-06 22:27:14 -0700 |
---|---|---|
committer | Evan Welsh <contact@evanwelsh.com> | 2021-08-06 22:27:14 -0700 |
commit | 8c585c9706b19c213682af9319789642cdcb15c6 (patch) | |
tree | 279c195012b63f744a01cb71f5b5f0809c0589f2 | |
parent | 469b38f367ed9707f2c17a1bdeb810d9c193e462 (diff) | |
download | gjs-ewlsh/fix-function-pointers.tar.gz |
Some really rough work on fixing function pointer supportewlsh/fix-function-pointers
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
-rw-r--r-- | gi/arg-cache.cpp | 21 | ||||
-rw-r--r-- | gi/arg-cache.h | 4 | ||||
-rw-r--r-- | gi/function.cpp | 225 | ||||
-rw-r--r-- | gi/function.h | 3 | ||||
-rw-r--r-- | 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_ @@ -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 |