/* Fundamental marshaller closure. */ static int marshal_fundamental_marshaller (lua_State *L) { gpointer obj; gboolean get_mode = lua_isnone (L, 3); GValue *value; lgi_type_get_repotype (L, G_TYPE_VALUE, NULL); lgi_record_2c (L, 1, &value, FALSE, FALSE, FALSE, FALSE); if (get_mode) { /* Get fundamental from value. */ GIObjectInfoGetValueFunction get_value = lua_touserdata (L, lua_upvalueindex (1)); obj = get_value (value); lgi_object_2lua (L, obj, FALSE, FALSE); return 1; } else { /* Set fundamental to value. */ GIObjectInfoSetValueFunction set_value = lua_touserdata (L, lua_upvalueindex (2)); obj = lgi_object_2c (L, 3, G_TYPE_INVALID, FALSE, FALSE, FALSE); set_value (value, obj); return 0; } }
/* Converts either GType or gi.info into repotype table. */ static int core_repotype (lua_State *L) { GType gtype = G_TYPE_INVALID; GIBaseInfo **info = lgi_udata_test (L, 1, LGI_GI_INFO); if (!info) gtype = lgi_type_get_gtype (L, 1); lgi_type_get_repotype (L, gtype, info ? *info : NULL); return 1; }
static void marshal_2lua_error (lua_State *L, GITransfer xfer, GError *err) { if (err == NULL) lua_pushnil (L); else { /* Wrap error instance with GLib.Error record. */ lgi_type_get_repotype (L, G_TYPE_ERROR, NULL); lgi_record_2lua (L, err, xfer != GI_TRANSFER_NOTHING, 0); } }
/* This is workaround for missing glib function, which should look like this: void g_closure_set_marshal_with_data (GClosure *closure, GClosureMarshal marshal, gpointer user_data, GDestroyNotify destroy_notify); Such method would be introspectable. */ static int marshal_closure_set_marshal (lua_State *L) { GClosure *closure; gpointer user_data; GClosureMarshal marshal; GIBaseInfo *ci; ci = g_irepository_find_by_name (NULL, "GObject", "ClosureMarshal"); lgi_type_get_repotype (L, G_TYPE_CLOSURE, NULL); lgi_record_2c (L, 1, &closure, FALSE, FALSE, FALSE, FALSE); user_data = lgi_closure_allocate (L, 1); lgi_callable_create (L, ci, NULL); marshal = lgi_closure_create (L, user_data, 2, FALSE); g_closure_set_marshal (closure, marshal); g_closure_add_invalidate_notifier (closure, user_data, gclosure_destroy); return 0; }
/* Marshalls single value from Lua to GLib/C. */ int lgi_marshal_2c (lua_State *L, GITypeInfo *ti, GIArgInfo *ai, GITransfer transfer, gpointer target, int narg, int parent, GICallableInfo *ci, void **args) { int nret = 0; gboolean optional = (parent == LGI_PARENT_CALLER_ALLOC) || (ai == NULL || (g_arg_info_is_optional (ai) || g_arg_info_may_be_null (ai))); GITypeTag tag = g_type_info_get_tag (ti); GIArgument *arg = target; /* Convert narg stack position to absolute one, because during marshalling some temporary items might be pushed to the stack, which would disrupt relative stack addressing of the value. */ lgi_makeabs(L, narg); switch (tag) { case GI_TYPE_TAG_BOOLEAN: { gboolean result; result = lua_toboolean (L, narg) ? TRUE : FALSE; if (parent == LGI_PARENT_FORCE_POINTER) arg->v_pointer = GINT_TO_POINTER (result); else if (parent == LGI_PARENT_IS_RETVAL) { ReturnUnion *ru = (ReturnUnion *) arg; ru->s = result; } else arg->v_boolean = result; break; } case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: { /* Retrieve number from given position. */ lua_Number num = (optional && lua_isnoneornil (L, narg)) ? 0 : luaL_checknumber (L, narg); /* Marshalling float/double into pointer target is not possible. */ g_return_val_if_fail (parent != LGI_PARENT_FORCE_POINTER, 0); /* Store read value into chosen target. */ if (tag == GI_TYPE_TAG_FLOAT) arg->v_float = (float) num; else arg->v_double = (double) num; break; } case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: { gchar *str = NULL; int type = lua_type (L, narg); if (type == LUA_TLIGHTUSERDATA) str = lua_touserdata (L, narg); else if (!optional || (type != LUA_TNIL && type != LUA_TNONE)) { if (type == LUA_TUSERDATA) str = (gchar *) lgi_udata_test (L, narg, LGI_BYTES_BUFFER); if (str == NULL) str = (gchar *) luaL_checkstring (L, narg); } if (tag == GI_TYPE_TAG_FILENAME) { /* Convert from UTF-8 to filename encoding. */ if (str) { str = g_filename_from_utf8 (str, -1, NULL, NULL, NULL); if (transfer != GI_TRANSFER_EVERYTHING) { /* Create temporary object on the stack which will destroy the allocated temporary filename. */ *lgi_guard_create (L, g_free) = (gpointer) str; nret = 1; } } } else if (transfer == GI_TRANSFER_EVERYTHING) str = g_strdup (str); if (parent == LGI_PARENT_FORCE_POINTER) arg->v_pointer = str; else arg->v_string = str; } break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (info); int info_guard; lgi_gi_info_new (L, info); info_guard = lua_gettop (L); switch (type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: /* If the argument is not numeric, convert to number first. Use enum/flags 'constructor' to do this. */ if (lua_type (L, narg) != LUA_TNUMBER) { lgi_type_get_repotype (L, G_TYPE_INVALID, info); lua_pushvalue (L, narg); lua_call (L, 1, 1); narg = -1; } /* Directly store underlying value. */ marshal_2c_int (L, g_enum_info_get_storage_type (info), arg, narg, optional, parent); /* Remove the temporary value, to keep stack balanced. */ if (narg == -1) lua_pop (L, 1); break; case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_UNION: { /* Ideally the g_type_info_is_pointer() should be sufficient here, but there is some gobject-introspection quirk that some struct arguments might not be marked as pointers (e.g. g_variant_equals(), which has ctype of gconstpointer, and thus logic in girparser.c which sets is_pointer attribute fails). Workaround it by checking also argument type - structs as C function arguments are always passed as pointers. */ gboolean by_value = parent != LGI_PARENT_FORCE_POINTER && ((!g_type_info_is_pointer (ti) && ai == NULL) || parent == LGI_PARENT_CALLER_ALLOC); lgi_type_get_repotype (L, G_TYPE_INVALID, info); lgi_record_2c (L, narg, target, by_value, transfer != GI_TRANSFER_NOTHING, optional, FALSE); break; } case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: { arg->v_pointer = lgi_object_2c (L, narg, g_registered_type_info_get_g_type (info), optional, FALSE, transfer != GI_TRANSFER_NOTHING); break; } case GI_INFO_TYPE_CALLBACK: nret = marshal_2c_callable (L, info, ai, &arg->v_pointer, narg, optional, ci, args); break; default: g_assert_not_reached (); } lua_remove (L, info_guard); } break; case GI_TYPE_TAG_ARRAY: { gssize size; GIArrayType atype = g_type_info_get_array_type (ti); nret = marshal_2c_array (L, ti, atype, &arg->v_pointer, &size, narg, optional, transfer); /* Fill in array length argument, if it is specified. */ if (atype == GI_ARRAY_TYPE_C) array_get_or_set_length (ti, NULL, size, ci, args); break; } case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: nret = marshal_2c_list (L, ti, tag, &arg->v_pointer, narg, transfer); break; case GI_TYPE_TAG_GHASH: nret = marshal_2c_hash (L, ti, (GHashTable **) &arg->v_pointer, narg, optional, transfer); break; case GI_TYPE_TAG_VOID: if (g_type_info_is_pointer (ti)) { /* Check and marshal according to real Lua type. */ if (lua_isnoneornil (L, narg)) /* nil -> NULL. */ arg->v_pointer = NULL; if (lua_type (L, narg) == LUA_TSTRING) /* Use string directly. */ arg->v_pointer = (gpointer) lua_tostring (L, narg); else { int type = lua_type (L, narg); if (type == LUA_TLIGHTUSERDATA) /* Generic pointer. */ arg->v_pointer = lua_touserdata (L, narg); else { /* Check memory buffer. */ arg->v_pointer = lgi_udata_test (L, narg, LGI_BYTES_BUFFER); if (!arg->v_pointer) { /* Check object. */ arg->v_pointer = lgi_object_2c (L, narg, G_TYPE_INVALID, FALSE, TRUE, FALSE); if (!arg->v_pointer) { /* Check any kind of record. */ lua_pushnil (L); lgi_record_2c (L, narg, &arg->v_pointer, FALSE, FALSE, FALSE, TRUE); } } } } } break; default: marshal_2c_int (L, tag, arg, narg, optional, parent); } return nret; }
/* Container marshaller function. */ static int marshal_container_marshaller (lua_State *L) { GValue *value; GITypeInfo **ti; GITypeTag tag; GITransfer transfer; gpointer data; int nret = 0; gboolean get_mode = lua_isnone (L, 3); /* Get GValue to operate on. */ lgi_type_get_repotype (L, G_TYPE_VALUE, NULL); lgi_record_2c (L, 1, &value, FALSE, FALSE, FALSE, FALSE); /* Get raw pointer from the value. */ if (get_mode) { if (G_VALUE_TYPE (value) == G_TYPE_POINTER) data = g_value_get_pointer (value); else data = g_value_get_boxed (value); } /* Get info and transfer from upvalue. */ ti = lua_touserdata (L, lua_upvalueindex (1)); tag = g_type_info_get_tag (*ti); transfer = lua_tointeger (L, lua_upvalueindex (2)); switch (tag) { case GI_TYPE_TAG_ARRAY: { GIArrayType atype = g_type_info_get_array_type (*ti); gssize size = -1; if (get_mode) { if (lua_type (L, 2) == LUA_TTABLE) { lua_getfield (L, 2, "length"); size = luaL_optinteger (L, -1, -1); lua_pop (L, 1); } marshal_2lua_array (L, *ti, GI_DIRECTION_OUT, atype, transfer, data, size, 0); } else { nret = marshal_2c_array (L, *ti, atype, &data, &size, 3, FALSE, transfer); if (lua_type (L, 2) == LUA_TTABLE) { lua_pushnumber (L, size); lua_setfield (L, 2, "length"); } } break; } case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GLIST: if (get_mode) marshal_2lua_list (L, *ti, GI_DIRECTION_OUT, tag, transfer, data); else nret = marshal_2c_list (L, *ti, tag, &data, 3, transfer); break; case GI_TYPE_TAG_GHASH: if (get_mode) marshal_2lua_hash (L, *ti, GI_DIRECTION_OUT, transfer, data); else nret = marshal_2c_hash (L, *ti, (GHashTable **) &data, 3, FALSE, transfer); break; default: g_assert_not_reached (); } /* Store result pointer to the value. */ if (!get_mode) { if (G_VALUE_TYPE (value) == G_TYPE_POINTER) g_value_set_pointer (value, data); else g_value_set_boxed (value, data); } /* If there are any temporary objects, try to store them into attrs.keepalive table, if it is present. */ if (!lua_isnoneornil (L, 2)) { lua_getfield (L, 2, "keepalive"); if (!lua_isnil (L, -1)) for (lua_insert (L, -nret - 1); nret > 0; nret--) { lua_pushnumber (L, lua_objlen (L, -nret - 1)); lua_insert (L, -2); lua_settable (L, -nret - 3); lua_pop (L, 1); } else lua_pop (L, nret); lua_pop (L, 1); } else lua_pop (L, nret); return get_mode ? 1 : 0; }
/* Marshalls single value from GLib/C to Lua. Returns 1 if something was pushed to the stack. */ void lgi_marshal_2lua (lua_State *L, GITypeInfo *ti, GIArgInfo *ai, GIDirection dir, GITransfer transfer, gpointer source, int parent, GICallableInfo *ci, void **args) { gboolean own = (transfer != GI_TRANSFER_NOTHING); GITypeTag tag = g_type_info_get_tag (ti); GIArgument *arg = source; /* Make sure that parent is absolute index so that it is fixed even when we add/remove from the stack. */ lgi_makeabs (L, parent); switch (tag) { case GI_TYPE_TAG_VOID: if (g_type_info_is_pointer (ti)) /* Marshal pointer to simple lightuserdata. */ lua_pushlightuserdata (L, arg->v_pointer); else lua_pushnil (L); break; case GI_TYPE_TAG_BOOLEAN: if (parent == LGI_PARENT_IS_RETVAL) { ReturnUnion *ru = (ReturnUnion *) arg; ru->arg.v_boolean = ru->s; } lua_pushboolean (L, arg->v_boolean); break; case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: g_return_if_fail (parent != LGI_PARENT_FORCE_POINTER); lua_pushnumber (L, (tag == GI_TYPE_TAG_FLOAT) ? arg->v_float : arg->v_double); break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: { gchar *str = (parent == LGI_PARENT_FORCE_POINTER) ? arg->v_pointer : arg->v_string; if (tag == GI_TYPE_TAG_FILENAME && str != NULL) { gchar *utf8 = g_filename_to_utf8 (str, -1, NULL, NULL, NULL); lua_pushstring (L, utf8); g_free (utf8); } else lua_pushstring (L, str); if (transfer == GI_TRANSFER_EVERYTHING) g_free (str); break; } case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (info); int info_guard; lgi_gi_info_new (L, info); info_guard = lua_gettop (L); switch (type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: /* Prepare repotable of enum/flags on the stack. */ lgi_type_get_repotype (L, G_TYPE_INVALID, info); /* Unmarshal the numeric value. */ marshal_2lua_int (L, g_enum_info_get_storage_type (info), arg, parent); /* Get symbolic value from the table. */ lua_gettable (L, -2); /* Remove the table from the stack. */ lua_remove (L, -2); break; case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_UNION: { gboolean by_ref = parent == LGI_PARENT_FORCE_POINTER || g_type_info_is_pointer (ti); if (parent < LGI_PARENT_CALLER_ALLOC && by_ref) parent = 0; lgi_type_get_repotype (L, G_TYPE_INVALID, info); lgi_record_2lua (L, by_ref ? arg->v_pointer : source, own, parent); break; } case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: /* Avoid sinking for input arguments, because it wreaks havoc to input arguments of vfunc callbacks during InitiallyUnowned construction phase. */ lgi_object_2lua (L, arg->v_pointer, own, dir == GI_DIRECTION_IN); break; case GI_INFO_TYPE_CALLBACK: if (arg->v_pointer == NULL) lua_pushnil (L); else { lgi_callable_create (L, info, arg->v_pointer); if (ai != NULL && args != NULL) { gint closure = g_arg_info_get_closure (ai); if (closure >= 0) { /* Store context associated with the callback to the callback object. */ GIArgument *arg = args[closure]; lua_pushlightuserdata (L, arg->v_pointer); lua_setfield (L, -2, "user_data"); } } } break; default: g_assert_not_reached (); } lua_remove (L, info_guard); } break; case GI_TYPE_TAG_ARRAY: { GIArrayType atype = g_type_info_get_array_type (ti); gssize size = -1; array_get_or_set_length (ti, &size, 0, ci, args); marshal_2lua_array (L, ti, dir, atype, transfer, arg->v_pointer, size, parent); } break; case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GLIST: marshal_2lua_list (L, ti, dir, tag, transfer, arg->v_pointer); break; case GI_TYPE_TAG_GHASH: marshal_2lua_hash (L, ti, dir, transfer, arg->v_pointer); break; case GI_TYPE_TAG_ERROR: marshal_2lua_error (L, transfer, arg->v_pointer); break; default: marshal_2lua_int (L, tag, arg, parent); } }
gboolean lgi_marshal_2c_caller_alloc (lua_State *L, GITypeInfo *ti, GIArgument *val, int pos) { gboolean handled = FALSE; switch (g_type_info_get_tag (ti)) { case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *ii = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (ii); if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_UNION) { if (pos == 0) { lgi_type_get_repotype (L, G_TYPE_INVALID, ii); val->v_pointer = lgi_record_new (L, 1, FALSE); } handled = TRUE; } g_base_info_unref (ii); break; } case GI_TYPE_TAG_ARRAY: { if (g_type_info_get_array_type (ti) == GI_ARRAY_TYPE_C) { gpointer *array_guard; if (pos == 0) { gssize elt_size, size; /* Currently only fixed-size arrays are supported. */ elt_size = array_get_elt_size (g_type_info_get_param_type (ti, 0)); size = g_type_info_get_array_fixed_size (ti); g_assert (size > 0); /* Allocate underlying array. It is temporary, existing only for the duration of the call. */ array_guard = lgi_guard_create (L, (GDestroyNotify) g_array_unref); *array_guard = g_array_sized_new (FALSE, FALSE, elt_size, size); g_array_set_size (*array_guard, size); } else { /* Convert the allocated array into Lua table with contents. We have to do it in-place. */ /* Make sure that pos is absolute, so that stack shuffling below does not change the element it points to. */ if (pos < 0) pos += lua_gettop (L) + 1; /* Get GArray from the guard and unmarshal it as a full GArray into Lua. */ array_guard = lua_touserdata (L, pos); marshal_2lua_array (L, ti, GI_DIRECTION_OUT, GI_ARRAY_TYPE_ARRAY, GI_TRANSFER_EVERYTHING, *array_guard, -1, pos); /* Deactivate old guard, everything was marshalled into the newly created and marshalled table. */ *array_guard = NULL; /* Switch old value with the new data. */ lua_replace (L, pos); } handled = TRUE; } break; } default: break; } return handled; }