This is our main entry point for ffi_closure callbacks. ffi_prep_closure() is * doing pure magic and replaces the original function call with this one which * gives us the ffi arguments, a place to store the return value and our use * data. In other words, everything we need to call the JS function and get the * return value back. */
| 300 | * return value back. |
| 301 | */ |
| 302 | void GjsCallbackTrampoline::callback_closure(GIArgument** args, void* result) { |
| 303 | GI::StackTypeInfo ret_type; |
| 304 | |
| 305 | // Fill in the result with some hopefully neutral value |
| 306 | m_info.load_return_type(&ret_type); |
| 307 | if (ret_type.tag() != GI_TYPE_TAG_VOID) { |
| 308 | GIArgument argument = {}; |
| 309 | gjs_arg_unset(&argument); |
| 310 | set_return_ffi_arg_from_gi_argument(ret_type, result, &argument); |
| 311 | } |
| 312 | |
| 313 | if (G_UNLIKELY(!is_valid())) { |
| 314 | warn_about_illegal_js_callback( |
| 315 | "during shutdown", |
| 316 | "destroying a Clutter actor or GTK widget with ::destroy signal " |
| 317 | "connected, or using the destroy(), dispose(), or remove() vfuncs", |
| 318 | true); |
| 319 | return; |
| 320 | } |
| 321 | |
| 322 | JSContext* cx = this->cx(); |
| 323 | GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx); |
| 324 | if (JS::RuntimeHeapIsCollecting()) { |
| 325 | warn_about_illegal_js_callback( |
| 326 | "during garbage collection", |
| 327 | "destroying a Clutter actor or GTK widget with ::destroy signal " |
| 328 | "connected, or using the destroy(), dispose(), or remove() vfuncs", |
| 329 | true); |
| 330 | return; |
| 331 | } |
| 332 | |
| 333 | if (G_UNLIKELY(!gjs->is_owner_thread())) { |
| 334 | warn_about_illegal_js_callback("on a different thread", |
| 335 | "an API not intended to be used in JS", |
| 336 | false); |
| 337 | return; |
| 338 | } |
| 339 | |
| 340 | JSAutoRealm ar{cx, callable()}; |
| 341 | |
| 342 | unsigned n_args = m_info.n_args(); |
| 343 | |
| 344 | auto cleanup = mozilla::MakeScopeExit([this, gjs]() { |
| 345 | if (this->m_scope == GI_SCOPE_TYPE_ASYNC) { |
| 346 | // We don't release the trampoline here as we've an extra ref that |
| 347 | // has been set in gjs_marshal_callback_in() |
| 348 | gjs_debug_closure("Saving async closure for gc cleanup %p", this); |
| 349 | gjs->async_closure_enqueue_for_gc(this); |
| 350 | } |
| 351 | gjs->schedule_gc_if_needed(); |
| 352 | }); |
| 353 | |
| 354 | JS::RootedObject this_object{cx}; |
| 355 | unsigned c_args_offset = 0; |
| 356 | GObject* gobj = nullptr; |
| 357 | if (m_is_vfunc) { |
| 358 | gobj = G_OBJECT(gjs_arg_get<GObject*>(args[0])); |
| 359 | if (gobj) { |
no test coverage detected