/* 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 int marshal_callback (lua_State *L) { gpointer user_data, addr; GICallableInfo **ci; user_data = lgi_closure_allocate (L, 1); *lgi_guard_create (L, lgi_closure_destroy) = user_data; if (lua_istable (L, 1)) lgi_callable_parse (L, 1); else { ci = lgi_udata_test (L, 1, LGI_GI_INFO); lgi_callable_create (L, *ci, NULL); } addr = lgi_closure_create (L, user_data, 2, FALSE); lua_pushlightuserdata (L, addr); return 2; }
/* 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 array from Lua to C. Returns number of temporary elements pushed to the stack. */ static int marshal_2c_array (lua_State *L, GITypeInfo *ti, GIArrayType atype, gpointer *out_array, gssize *out_size, int narg, gboolean optional, GITransfer transfer) { GITypeInfo* eti; gssize objlen, esize; gint index, vals = 0, to_pop, eti_guard; GITransfer exfer = (transfer == GI_TRANSFER_EVERYTHING ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING); gboolean zero_terminated; GArray *array = NULL; /* Represent nil as NULL array. */ if (optional && lua_isnoneornil (L, narg)) { *out_size = 0; *out_array = NULL; } else { /* Get element type info, create guard for it. */ eti = g_type_info_get_param_type (ti, 0); lgi_gi_info_new (L, eti); eti_guard = lua_gettop (L); esize = array_get_elt_size (eti); /* Check the type. If this is C-array of byte-sized elements, we can try special-case and accept strings or buffers. */ *out_array = NULL; if (lua_type (L, narg) != LUA_TTABLE && esize == 1 && atype == GI_ARRAY_TYPE_C) { size_t size = 0; *out_array = lgi_udata_test (L, narg, LGI_BYTES_BUFFER); if (*out_array) size = lua_objlen (L, narg); else *out_array = (gpointer *) lua_tolstring (L, narg, &size); if (transfer != GI_TRANSFER_NOTHING) *out_array = g_memdup (*out_array, size); *out_size = size; } if (!*out_array) { /* Otherwise, we allow only tables. */ luaL_checktype (L, narg, LUA_TTABLE); /* Find out how long array should we allocate. */ zero_terminated = g_type_info_is_zero_terminated (ti); objlen = lua_objlen (L, narg); *out_size = g_type_info_get_array_fixed_size (ti); if (atype != GI_ARRAY_TYPE_C || *out_size < 0) *out_size = objlen; else if (*out_size < objlen) objlen = *out_size; /* Allocate the array and wrap it into the userdata guard, if needed. */ if (*out_size > 0 || zero_terminated) { array = g_array_sized_new (zero_terminated, TRUE, esize, *out_size); g_array_set_size (array, *out_size); *lgi_guard_create (L, (GDestroyNotify) (transfer == GI_TRANSFER_EVERYTHING ? array_detach : g_array_unref)) = array; vals = 1; } /* Iterate through Lua array and fill GArray accordingly. */ for (index = 0; index < objlen; index++) { lua_pushnumber (L, index + 1); lua_gettable (L, narg); /* Marshal element retrieved from the table into target array. */ to_pop = lgi_marshal_2c (L, eti, NULL, exfer, array->data + index * esize, -1, 0, NULL, NULL); /* Remove temporary element from the stack. */ lua_remove (L, - to_pop - 1); /* Remember that some more temp elements could be pushed. */ vals += to_pop; } /* Return either GArray or direct pointer to the data, according to the array type. */ *out_array = (atype == GI_ARRAY_TYPE_ARRAY || array == NULL) ? (void *) array : (void *) array->data; } lua_remove (L, eti_guard); } return vals; }
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; }