summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Welsh <contact@evanwelsh.com>2021-08-06 22:27:14 -0700
committerEvan Welsh <contact@evanwelsh.com>2021-08-06 22:27:14 -0700
commit8c585c9706b19c213682af9319789642cdcb15c6 (patch)
tree279c195012b63f744a01cb71f5b5f0809c0589f2
parent469b38f367ed9707f2c17a1bdeb810d9c193e462 (diff)
downloadgjs-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.cpp21
-rw-r--r--gi/arg-cache.h4
-rw-r--r--gi/function.cpp225
-rw-r--r--gi/function.h3
-rw-r--r--log.js5
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