void * lgi_udata_test (lua_State *L, int narg, const char *name) { void *udata = NULL; luaL_checkstack (L, 2, ""); lgi_makeabs (L, narg); if (lua_getmetatable (L, narg)) { luaL_getmetatable (L, name); if (lua_equal (L, -1, -2)) udata = lua_touserdata (L, narg); lua_pop (L, 2); } return udata; }
GType lgi_type_get_gtype (lua_State *L, int narg) { /* Handle simple cases natively, forward to Lua implementation for the rest. */ switch (lua_type (L, narg)) { case LUA_TNIL: case LUA_TNONE: return G_TYPE_INVALID; case LUA_TNUMBER: return lua_tonumber (L, narg); case LUA_TLIGHTUSERDATA: return (GType) lua_touserdata (L, narg); case LUA_TSTRING: return g_type_from_name (lua_tostring (L, narg)); case LUA_TTABLE: { GType gtype; lgi_makeabs (L, narg); lua_pushstring (L, "_gtype"); lua_rawget (L, narg); gtype = lgi_type_get_gtype (L, -1); lua_pop (L, 1); return gtype; } default: return luaL_error (L, "GType expected, got %s", lua_typename (L, lua_type (L, narg))); } }
int lgi_marshal_field (lua_State *L, gpointer object, gboolean getmode, int parent_arg, int field_arg, int val_arg) { GITypeInfo *ti; int to_remove, nret; /* Check the type of the field information. */ if (lgi_udata_test (L, field_arg, LGI_GI_INFO)) { GIFieldInfo **fi = lua_touserdata (L, field_arg); GIFieldInfoFlags flags; /* Check, whether field is readable/writable. */ flags = g_field_info_get_flags (*fi); if ((flags & (getmode ? GI_FIELD_IS_READABLE : GI_FIELD_IS_WRITABLE)) == 0) { /* Check, whether parent did not disable access checks completely. */ lua_getfield (L, -1, "_allow"); if (!lua_toboolean (L, -1)) { /* Prepare proper error message. */ lua_concat (L, lgi_type_get_name (L, g_base_info_get_container (*fi))); return luaL_error (L, "%s: field `%s' is not %s", lua_tostring (L, -1), g_base_info_get_name (*fi), getmode ? "readable" : "writable"); } lua_pop (L, 1); } /* Map GIArgument to proper memory location, get typeinfo of the field and perform actual marshalling. */ object = (char *) object + g_field_info_get_offset (*fi); ti = g_field_info_get_type (*fi); lgi_gi_info_new (L, ti); to_remove = lua_gettop (L); } else { /* Consult field table, get kind of field and offset. */ int kind; lgi_makeabs (L, field_arg); luaL_checktype (L, field_arg, LUA_TTABLE); lua_rawgeti (L, field_arg, 1); object = (char *) object + lua_tointeger (L, -1); lua_rawgeti (L, field_arg, 2); kind = lua_tonumber (L, -1); lua_pop (L, 2); /* Load type information from the table and decide how to handle it according to 'kind' */ lua_rawgeti (L, field_arg, 3); switch (kind) { case 0: /* field[3] contains typeinfo, load it and fall through. */ ti = *(GITypeInfo **) luaL_checkudata (L, -1, LGI_GI_INFO); to_remove = lua_gettop (L); break; case 1: case 2: { GIArgument *arg = (GIArgument *) object; if (getmode) { if (kind == 1) { object = arg->v_pointer; parent_arg = 0; } lgi_record_2lua (L, object, FALSE, parent_arg); return 1; } else { g_assert (kind == 1); lgi_record_2c (L, val_arg, arg->v_pointer, FALSE, TRUE, FALSE, FALSE); return 0; } break; } case 3: { /* Get the typeinfo for marshalling the numeric enum value. */ lua_rawgeti (L, field_arg, 4); ti = *(GITypeInfo **) luaL_checkudata (L, -1, LGI_GI_INFO); if (getmode) { /* Use typeinfo to unmarshal numeric value. */ lgi_marshal_2lua (L, ti, NULL, GI_DIRECTION_OUT, GI_TRANSFER_NOTHING, object, 0, NULL, NULL); /* Replace numeric field with symbolic value. */ lua_gettable (L, -3); lua_replace (L, -3); lua_pop (L, 1); return 1; } else { /* Convert enum symbol to numeric value. */ if (lua_type (L, val_arg != LUA_TNUMBER)) { lua_pushvalue (L, -1); lua_pushvalue (L, val_arg); lua_call (L, 1, 1); lua_replace (L, val_arg); } /* Use typeinfo to marshal the numeric value. */ lgi_marshal_2c (L, ti, NULL, GI_TRANSFER_NOTHING, object, val_arg, 0, NULL, NULL); lua_pop (L, 2); return 0; } } default: return luaL_error (L, "field has bad kind %d", kind); } } if (getmode) { lgi_marshal_2lua (L, ti, NULL, GI_DIRECTION_OUT, GI_TRANSFER_NOTHING, object, parent_arg, NULL, NULL); nret = 1; } else { lgi_marshal_2c (L, ti, NULL, GI_TRANSFER_EVERYTHING, object, val_arg, 0, NULL, NULL); nret = 0; } lua_remove (L, to_remove); return nret; }
/* 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; }
/* 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); } }