static gboolean peas_extension_gjs_call (PeasExtensionWrapper *exten, GType exten_type, GICallableInfo *func_info, const gchar *method_name, GIArgument *args, GIArgument *retval) { PeasExtensionGjs *gexten = PEAS_EXTENSION_GJS (exten); gboolean success = FALSE; jsval js_method, js_retval; jsval *js_args; CachedArg *arg_cache; gint i, n_args, nth_out_arg; gint n_in_args = 0; gint n_out_args = 0; gint cached_args = 0; /* Fetch the JS method we want to call */ if (!JS_GetProperty (gexten->js_context, gexten->js_object, method_name, &js_method) || JSVAL_IS_VOID (js_method)) { g_warning ("Method '%s.%s' was not found", g_type_name (exten_type), method_name); return FALSE; } if (JSVAL_IS_NULL (js_method) || !JSVAL_IS_OBJECT (js_method) || !JS_ObjectIsFunction (gexten->js_context, JSVAL_TO_OBJECT (js_method))) { g_warning ("Method '%s.%s' in not a function", g_type_name (exten_type), method_name); return FALSE; } n_args = g_callable_info_get_n_args (func_info); if (n_args < 0) { g_warn_if_fail (n_args >= 0); return FALSE; } js_args = g_newa (jsval, n_args); arg_cache = g_newa (CachedArg, n_args + 1); /* Return value is an out arg */ g_callable_info_load_return_type (func_info, &arg_cache[0].type_info); if (g_type_info_get_tag (&arg_cache[0].type_info) != GI_TYPE_TAG_VOID) { ++n_out_args; arg_cache[cached_args++].ptr = &retval->v_pointer; } /* Handle the arguments */ for (i = 0; i < n_args; ++i, ++cached_args) { GIDirection direction; g_callable_info_load_arg (func_info, i, &arg_cache[cached_args].arg_info); direction = g_arg_info_get_direction (&arg_cache[cached_args].arg_info); g_arg_info_load_type (&arg_cache[cached_args].arg_info, &arg_cache[cached_args].type_info); if (direction == GI_DIRECTION_IN && !gjs_value_from_g_argument (gexten->js_context, &js_args[n_in_args++], &arg_cache[cached_args].type_info, &args[i], TRUE)) { g_warning ("Error failed to convert argument '%s'", g_base_info_get_name (&arg_cache[cached_args].arg_info)); return FALSE; } if (direction == GI_DIRECTION_INOUT) { GIArgument arg; peas_gi_pointer_to_argument (&arg_cache[cached_args].type_info, args[i].v_pointer, &arg); if (!gjs_value_from_g_argument (gexten->js_context, &js_args[n_in_args++], &arg_cache[cached_args].type_info, &arg, TRUE)) { g_warning ("Error failed to convert argument '%s'", g_base_info_get_name (&arg_cache[cached_args].arg_info)); return FALSE; } } if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { ++n_out_args; arg_cache[cached_args].ptr = args[i].v_pointer; } } success = JS_CallFunctionValue (gexten->js_context, gexten->js_object, js_method, n_in_args, js_args, &js_retval); if (!success) { if (!gjs_log_exception (gexten->js_context, NULL)) { g_warning ("Error while calling '%s.%s'", g_type_name (exten_type), method_name); } return FALSE; } /* First we need to release in argument */ for (i = 0; i < cached_args; ++i) { GIDirection direction; /* First cached argument may be the return value */ if (i == 0 && cached_args > n_args) continue; direction = g_arg_info_get_direction (&arg_cache[i].arg_info); if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { GITransfer transfer; transfer = g_arg_info_get_ownership_transfer (&arg_cache[i].arg_info); if (!gjs_g_argument_release_in_arg (gexten->js_context, transfer, &arg_cache[i].type_info, &args[i])) { g_warning ("Error failed to release IN argument '%s'", g_base_info_get_name (&arg_cache[i].arg_info)); } } } /* Check that we have a valid return value */ if (n_out_args > 1) { if (!JSVAL_IS_OBJECT (js_retval) || !JS_IsArrayObject (gexten->js_context, JSVAL_TO_OBJECT (js_retval))) { g_warning ("Error return value is not an array"); return FALSE; } } /* Set out arguments */ for (i = 0, nth_out_arg = 0; i < cached_args && success; ++i) { gboolean is_return_value; is_return_value = i == 0 && cached_args > n_args; /* Return value does not have a GIArgInfo and is always out */ if (!is_return_value) { GIDirection direction; direction = g_arg_info_get_direction (&arg_cache[i].arg_info); if (direction == GI_DIRECTION_IN) continue; } if (n_out_args == 1) { success = set_out_arg (gexten->js_context, func_info, is_return_value, &arg_cache[i].arg_info, &arg_cache[i].type_info, arg_cache[i].ptr, js_retval); break; } else if (n_out_args > 1) { jsval js_value; if (!JS_GetElement (gexten->js_context, JSVAL_TO_OBJECT (js_retval), nth_out_arg++, &js_value) || js_value == JSVAL_VOID) { g_warning ("Error failed to get out argument %i", nth_out_arg); return FALSE; } else { success = set_out_arg (gexten->js_context, func_info, is_return_value, &arg_cache[i].arg_info, &arg_cache[i].type_info, arg_cache[i].ptr, js_value); } } } return success; }
/* a hook on getting a property; set value_p to override property's value. * Return value is JS_FALSE on OOM/exception. */ static JSBool param_get_prop(JSContext *context, JSObject *obj, jsid id, jsval *value_p) { JSBool success; Param *priv; GParamSpec *pspec; char *name; GType gtype; GIObjectInfo *info = NULL, *parent_info = NULL; GIFieldInfo *field_info = NULL; GITypeInfo *type_info = NULL; GIArgument arg; if (!gjs_get_string_id(context, id, &name)) return JS_TRUE; /* not something we affect, but no error */ priv = priv_from_js(context, obj); if (priv == NULL) { g_free(name); return JS_FALSE; /* wrong class */ } success = JS_FALSE; pspec = priv->gparam; gtype = G_TYPE_FROM_INSTANCE(pspec); info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), gtype); if (info == NULL) { /* We may have a non-introspectable GParamSpec subclass here. Just return VOID. */ *value_p = JSVAL_VOID; success = JS_TRUE; goto out; } parent_info = g_object_info_get_parent(info); field_info = find_field_info(info, name); if (field_info == NULL) { /* Try it on the parent GParamSpec for generic GParamSpec properties. */ field_info = find_field_info(parent_info, name); } if (field_info == NULL) { *value_p = JSVAL_VOID; success = JS_TRUE; goto out; } type_info = g_field_info_get_type(field_info); if (!g_field_info_get_field(field_info, priv->gparam, &arg)) { gjs_throw(context, "Reading field %s.%s is not supported", g_base_info_get_name(info), g_base_info_get_name((GIBaseInfo*)field_info)); goto out; } if (!gjs_value_from_g_argument(context, value_p, type_info, &arg, TRUE)) goto out; success = JS_TRUE; out: if (field_info != NULL) g_base_info_unref((GIBaseInfo*)field_info); if (type_info != NULL) g_base_info_unref((GIBaseInfo*)type_info); if (info != NULL) g_base_info_unref((GIBaseInfo*)info); if (parent_info != NULL) g_base_info_unref((GIBaseInfo*)parent_info); g_free(name); return success; }
static JSBool boxed_field_getter (JSContext *context, JSObject *obj, jsid id, jsval *value) { Boxed *priv; GIFieldInfo *field_info; GITypeInfo *type_info; GArgument arg; gboolean success = FALSE; priv = priv_from_js(context, obj); if (!priv) return JS_FALSE; field_info = get_field_info(context, priv, id); if (!field_info) return JS_FALSE; type_info = g_field_info_get_type (field_info); if (priv->gboxed == NULL) { /* direct access to proto field */ gjs_throw(context, "Can't get field %s.%s from a prototype", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } if (!g_type_info_is_pointer (type_info) && g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface_info = g_type_info_get_interface(type_info); if (g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT || g_base_info_get_type (interface_info) == GI_INFO_TYPE_BOXED) { success = get_nested_interface_object (context, obj, priv, field_info, type_info, interface_info, value); g_base_info_unref ((GIBaseInfo *)interface_info); goto out; } g_base_info_unref ((GIBaseInfo *)interface_info); } if (!g_field_info_get_field (field_info, priv->gboxed, &arg)) { gjs_throw(context, "Reading field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } if (!gjs_value_from_g_argument (context, value, type_info, &arg, TRUE)) goto out; success = TRUE; out: g_base_info_unref ((GIBaseInfo *)field_info); g_base_info_unref ((GIBaseInfo *)type_info); return success; }
static JSBool gjs_value_from_g_value_internal(JSContext *context, jsval *value_p, const GValue *gvalue, gboolean no_copy, GSignalQuery *signal_query, gint arg_n) { GType gtype; gtype = G_VALUE_TYPE(gvalue); gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting gtype %s to jsval", g_type_name(gtype)); if (gtype == G_TYPE_STRING) { const char *v; v = g_value_get_string(gvalue); if (v == NULL) { gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting NULL string to JSVAL_NULL"); *value_p = JSVAL_NULL; } else { if (!gjs_string_from_utf8(context, v, -1, value_p)) return JS_FALSE; } } else if (gtype == G_TYPE_CHAR) { char v; v = g_value_get_schar(gvalue); *value_p = INT_TO_JSVAL(v); } else if (gtype == G_TYPE_UCHAR) { unsigned char v; v = g_value_get_uchar(gvalue); *value_p = INT_TO_JSVAL(v); } else if (gtype == G_TYPE_INT) { int v; v = g_value_get_int(gvalue); return JS_NewNumberValue(context, v, value_p); } else if (gtype == G_TYPE_UINT) { uint v; v = g_value_get_uint(gvalue); return JS_NewNumberValue(context, v, value_p); } else if (gtype == G_TYPE_DOUBLE) { double d; d = g_value_get_double(gvalue); return JS_NewNumberValue(context, d, value_p); } else if (gtype == G_TYPE_FLOAT) { double d; d = g_value_get_float(gvalue); return JS_NewNumberValue(context, d, value_p); } else if (gtype == G_TYPE_BOOLEAN) { gboolean v; v = g_value_get_boolean(gvalue); *value_p = BOOLEAN_TO_JSVAL(v); } else if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) { GObject *gobj; JSObject *obj; gobj = g_value_get_object(gvalue); obj = gjs_object_from_g_object(context, gobj); *value_p = OBJECT_TO_JSVAL(obj); } else if (gtype == G_TYPE_STRV) { if (!gjs_array_from_strv (context, value_p, g_value_get_boxed (gvalue))) { gjs_throw(context, "Failed to convert strv to array"); return JS_FALSE; } } else if (g_type_is_a(gtype, G_TYPE_HASH_TABLE) || g_type_is_a(gtype, G_TYPE_ARRAY) || g_type_is_a(gtype, G_TYPE_BYTE_ARRAY) || g_type_is_a(gtype, G_TYPE_PTR_ARRAY)) { gjs_throw(context, "Unable to introspect element-type of container in GValue"); return JS_FALSE; } else if (g_type_is_a(gtype, G_TYPE_BOXED) || g_type_is_a(gtype, G_TYPE_VARIANT)) { GjsBoxedCreationFlags boxed_flags; GIBaseInfo *info; void *gboxed; JSObject *obj; if (g_type_is_a(gtype, G_TYPE_BOXED)) gboxed = g_value_get_boxed(gvalue); else gboxed = g_value_get_variant(gvalue); boxed_flags = GJS_BOXED_CREATION_NONE; /* special case GError */ if (g_type_is_a(gtype, G_TYPE_ERROR)) { obj = gjs_error_from_gerror(context, gboxed, FALSE); *value_p = OBJECT_TO_JSVAL(obj); return TRUE; } /* The only way to differentiate unions and structs is from * their g-i info as both GBoxed */ info = g_irepository_find_by_gtype(g_irepository_get_default(), gtype); if (info == NULL) { gjs_throw(context, "No introspection information found for %s", g_type_name(gtype)); return JS_FALSE; } if (g_base_info_get_type(info) == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)info)) { JSBool ret; GIArgument arg; arg.v_pointer = gboxed; ret = gjs_struct_foreign_convert_from_g_argument(context, value_p, info, &arg); g_base_info_unref(info); return ret; } switch (g_base_info_get_type(info)) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: if (no_copy) boxed_flags |= GJS_BOXED_CREATION_NO_COPY; obj = gjs_boxed_from_c_struct(context, (GIStructInfo *)info, gboxed, boxed_flags); break; case GI_INFO_TYPE_UNION: obj = gjs_union_from_c_union(context, (GIUnionInfo *)info, gboxed); break; default: gjs_throw(context, "Unexpected introspection type %d for %s", g_base_info_get_type(info), g_type_name(gtype)); g_base_info_unref(info); return JS_FALSE; } *value_p = OBJECT_TO_JSVAL(obj); g_base_info_unref(info); } else if (g_type_is_a(gtype, G_TYPE_ENUM)) { return convert_int_to_enum(context, value_p, gtype, g_value_get_enum(gvalue)); } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { GParamSpec *gparam; JSObject *obj; gparam = g_value_get_param(gvalue); obj = gjs_param_from_g_param(context, gparam); *value_p = OBJECT_TO_JSVAL(obj); } else if (signal_query && g_type_is_a(gtype, G_TYPE_POINTER)) { JSBool res; GArgument arg; GIArgInfo *arg_info; GIBaseInfo *obj; GISignalInfo *signal_info; GITypeInfo type_info; obj = g_irepository_find_by_gtype(NULL, signal_query->itype); if (!obj) { gjs_throw(context, "Signal argument with GType %s isn't introspectable", g_type_name(signal_query->itype)); return JS_FALSE; } signal_info = g_object_info_find_signal((GIObjectInfo*)obj, signal_query->signal_name); if (!signal_info) { gjs_throw(context, "Unknown signal."); g_base_info_unref((GIBaseInfo*)obj); return JS_FALSE; } arg_info = g_callable_info_get_arg(signal_info, arg_n - 1); g_arg_info_load_type(arg_info, &type_info); arg.v_pointer = g_value_get_pointer(gvalue); res = gjs_value_from_g_argument(context, value_p, &type_info, &arg, TRUE); g_base_info_unref((GIBaseInfo*)arg_info); g_base_info_unref((GIBaseInfo*)signal_info); g_base_info_unref((GIBaseInfo*)obj); return res; } else if (g_type_is_a(gtype, G_TYPE_POINTER)) { gpointer pointer; pointer = g_value_get_pointer(gvalue); if (pointer == NULL) { *value_p = JSVAL_NULL; } else { gjs_throw(context, "Can't convert non-null pointer to JS value"); return JS_FALSE; } } else if (g_value_type_transformable(gtype, G_TYPE_DOUBLE)) { GValue double_value = { 0, }; double v; g_value_init(&double_value, G_TYPE_DOUBLE); g_value_transform(gvalue, &double_value); v = g_value_get_double(&double_value); return JS_NewNumberValue(context, v, value_p); } else if (g_value_type_transformable(gtype, G_TYPE_INT)) { GValue int_value = { 0, }; int v; g_value_init(&int_value, G_TYPE_INT); g_value_transform(gvalue, &int_value); v = g_value_get_int(&int_value); return JS_NewNumberValue(context, v, value_p); } else { gjs_throw(context, "Don't know how to convert GType %s to JavaScript object", g_type_name(gtype)); return JS_FALSE; } return JS_TRUE; }