static void array_c_to_ruby(const gchar **elements, GITypeInfo *type_info, VALUE rb_array) { gint n_elements; gboolean fixed_size_p; gboolean zero_terminated_p; n_elements = g_type_info_get_array_length(type_info); fixed_size_p = g_type_info_get_array_fixed_size(type_info); zero_terminated_p = g_type_info_is_zero_terminated(type_info); if (n_elements != -1) { gint i; for (i = 0; i < n_elements; i++) { rb_ary_push(rb_array, CSTR2RVAL(elements[i])); } } else if (zero_terminated_p) { for (; *elements; elements++) { rb_ary_push(rb_array, CSTR2RVAL(*elements)); } } else { rb_raise(rb_eNotImpError, "TODO: GIArgument(array)[c] -> Ruby: " "zero-terminated: %s " "fixed-size: %s " "length: %d", zero_terminated_p ? "true" : "false", fixed_size_p ? "true" : "false", n_elements); } }
static void marshal_2lua_array (lua_State *L, GITypeInfo *ti, GIDirection dir, GIArrayType atype, GITransfer transfer, gpointer array, gssize size, int parent) { GITypeInfo *eti; gssize len = 0, esize; gint index, eti_guard; char *data = NULL; /* Avoid propagating return value marshaling flag to array elements. */ if (parent == LGI_PARENT_IS_RETVAL) parent = 0; /* First of all, find out the length of the array. */ if (atype == GI_ARRAY_TYPE_ARRAY) { if (array) { len = ((GArray *) array)->len; data = ((GArray *) array)->data; } } else { data = array; if (g_type_info_is_zero_terminated (ti)) len = -1; else { len = g_type_info_get_array_fixed_size (ti); if (len == -1) /* Length of the array is dynamic, get it from other argument. */ len = size; } } /* Get array element type info, wrap it in the guard so that we don't leak 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); /* Note that we ignore is_pointer check for uint8 type. Although it is not exactly correct, we probably would not handle uint8* correctly anyway, this is strange type to use, and moreover this is workaround for g-ir-scanner bug which might mark elements of uint8 arrays as gconstpointer, thus setting is_pointer=true on it. See https://github.com/pavouk/lgi/issues/57 */ if (g_type_info_get_tag (eti) == GI_TYPE_TAG_UINT8) { /* UINT8 arrays are marshalled as Lua strings. */ if (len < 0) len = data ? strlen(data) : 0; lua_pushlstring (L, data, len); } else { if (array == NULL) { /* NULL array is represented by empty table for C arrays, nil for other types. */ if (atype == GI_ARRAY_TYPE_C) lua_newtable (L); else lua_pushnil (L); lua_remove (L, eti_guard); return; } /* Create Lua table which will hold the array. */ lua_createtable (L, len > 0 ? len : 0, 0); /* Iterate through array elements. */ for (index = 0; len < 0 || index < len; index++) { /* Get value from specified index. */ GIArgument *eval = (GIArgument *) (data + index * esize); /* If the array is zero-terminated, terminate now and don't include NULL entry. */ if (len < 0 && eval->v_pointer == NULL) break; /* Store value into the table. */ lgi_marshal_2lua (L, eti, NULL, dir, (transfer == GI_TRANSFER_EVERYTHING) ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING, eval, parent, NULL, NULL); lua_rawseti (L, -2, index + 1); } } /* If needed, free the original array. */ if (transfer != GI_TRANSFER_NOTHING) { if (atype == GI_ARRAY_TYPE_ARRAY) g_array_free (array, TRUE); else g_free (array); } lua_remove (L, eti_guard); }
GIArgument _pygi_argument_from_object (PyObject *object, GITypeInfo *type_info, GITransfer transfer) { GIArgument arg; GITypeTag type_tag; gpointer cleanup_data = NULL; memset(&arg, 0, sizeof(GIArgument)); type_tag = g_type_info_get_tag (type_info); /* Ignores cleanup data for now. */ if (_pygi_marshal_from_py_basic_type (object, &arg, type_tag, transfer, &cleanup_data) || PyErr_Occurred()) { return arg; } switch (type_tag) { case GI_TYPE_TAG_ARRAY: { Py_ssize_t length; gboolean is_zero_terminated; GITypeInfo *item_type_info; gsize item_size; GArray *array; GITransfer item_transfer; Py_ssize_t i; if (object == Py_None) { arg.v_pointer = NULL; break; } /* Note, strings are sequences, but we cannot accept them here */ if (!PySequence_Check (object) || #if PY_VERSION_HEX < 0x03000000 PyString_Check (object) || #endif PyUnicode_Check (object)) { PyErr_SetString (PyExc_TypeError, "expected sequence"); break; } length = PySequence_Length (object); if (length < 0) { break; } is_zero_terminated = g_type_info_is_zero_terminated (type_info); item_type_info = g_type_info_get_param_type (type_info, 0); /* we handle arrays that are really strings specially, see below */ if (g_type_info_get_tag (item_type_info) == GI_TYPE_TAG_UINT8) item_size = 1; else item_size = sizeof (GIArgument); array = g_array_sized_new (is_zero_terminated, FALSE, item_size, length); if (array == NULL) { g_base_info_unref ( (GIBaseInfo *) item_type_info); PyErr_NoMemory(); break; } if (g_type_info_get_tag (item_type_info) == GI_TYPE_TAG_UINT8 && PYGLIB_PyBytes_Check(object)) { memcpy(array->data, PYGLIB_PyBytes_AsString(object), length); array->len = length; goto array_success; } item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer; for (i = 0; i < length; i++) { PyObject *py_item; GIArgument item; py_item = PySequence_GetItem (object, i); if (py_item == NULL) { goto array_item_error; } item = _pygi_argument_from_object (py_item, item_type_info, item_transfer); Py_DECREF (py_item); if (PyErr_Occurred()) { goto array_item_error; } g_array_insert_val (array, i, item); continue; array_item_error: /* Free everything we have converted so far. */ _pygi_argument_release ( (GIArgument *) &array, type_info, GI_TRANSFER_NOTHING, GI_DIRECTION_IN); array = NULL; _PyGI_ERROR_PREFIX ("Item %zd: ", i); break; } array_success: arg.v_pointer = array; g_base_info_unref ( (GIBaseInfo *) item_type_info); break; } case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info; GIInfoType info_type; info = g_type_info_get_interface (type_info); info_type = g_base_info_get_type (info); switch (info_type) { case GI_INFO_TYPE_CALLBACK: /* This should be handled in invoke() */ g_assert_not_reached(); break; case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_UNION: { GType g_type; PyObject *py_type; gboolean is_foreign = (info_type == GI_INFO_TYPE_STRUCT) && (g_struct_info_is_foreign ((GIStructInfo *) info)); g_type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info); py_type = _pygi_type_import_by_gi_info ( (GIBaseInfo *) info); /* Note for G_TYPE_VALUE g_type: * This will currently leak the GValue that is allocated and * stashed in arg.v_pointer. Out argument marshaling for caller * allocated GValues already pass in memory for the GValue. * Further re-factoring is needed to fix this leak. * See: https://bugzilla.gnome.org/show_bug.cgi?id=693405 */ pygi_arg_struct_from_py_marshal (object, &arg, NULL, /*arg_name*/ info, /*interface_info*/ g_type, py_type, transfer, FALSE, /*copy_reference*/ is_foreign, g_type_info_is_pointer (type_info)); Py_DECREF (py_type); break; } case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: { PyObject *int_; int_ = PYGLIB_PyNumber_Long (object); if (int_ == NULL) { break; } arg.v_int = PYGLIB_PyLong_AsLong (int_); Py_DECREF (int_); break; } case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_OBJECT: /* An error within this call will result in a NULL arg */ pygi_arg_gobject_out_arg_from_py (object, &arg, transfer); break; default: g_assert_not_reached(); } g_base_info_unref (info); break; } case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: { Py_ssize_t length; GITypeInfo *item_type_info; GSList *list = NULL; GITransfer item_transfer; Py_ssize_t i; if (object == Py_None) { arg.v_pointer = NULL; break; } length = PySequence_Length (object); if (length < 0) { break; } item_type_info = g_type_info_get_param_type (type_info, 0); g_assert (item_type_info != NULL); item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer; for (i = length - 1; i >= 0; i--) { PyObject *py_item; GIArgument item; py_item = PySequence_GetItem (object, i); if (py_item == NULL) { goto list_item_error; } item = _pygi_argument_from_object (py_item, item_type_info, item_transfer); Py_DECREF (py_item); if (PyErr_Occurred()) { goto list_item_error; } if (type_tag == GI_TYPE_TAG_GLIST) { list = (GSList *) g_list_prepend ( (GList *) list, item.v_pointer); } else { list = g_slist_prepend (list, item.v_pointer); } continue; list_item_error: /* Free everything we have converted so far. */ _pygi_argument_release ( (GIArgument *) &list, type_info, GI_TRANSFER_NOTHING, GI_DIRECTION_IN); list = NULL; _PyGI_ERROR_PREFIX ("Item %zd: ", i); break; } arg.v_pointer = list; g_base_info_unref ( (GIBaseInfo *) item_type_info); break; } case GI_TYPE_TAG_GHASH: { Py_ssize_t length; PyObject *keys; PyObject *values; GITypeInfo *key_type_info; GITypeInfo *value_type_info; GITypeTag key_type_tag; GHashFunc hash_func; GEqualFunc equal_func; GHashTable *hash_table; GITransfer item_transfer; Py_ssize_t i; if (object == Py_None) { arg.v_pointer = NULL; break; } length = PyMapping_Length (object); if (length < 0) { break; } keys = PyMapping_Keys (object); if (keys == NULL) { break; } values = PyMapping_Values (object); if (values == NULL) { Py_DECREF (keys); break; } key_type_info = g_type_info_get_param_type (type_info, 0); g_assert (key_type_info != NULL); value_type_info = g_type_info_get_param_type (type_info, 1); g_assert (value_type_info != NULL); key_type_tag = g_type_info_get_tag (key_type_info); switch (key_type_tag) { case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: hash_func = g_str_hash; equal_func = g_str_equal; break; default: hash_func = NULL; equal_func = NULL; } hash_table = g_hash_table_new (hash_func, equal_func); if (hash_table == NULL) { PyErr_NoMemory(); goto hash_table_release; } item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer; for (i = 0; i < length; i++) { PyObject *py_key; PyObject *py_value; GIArgument key; GIArgument value; py_key = PyList_GET_ITEM (keys, i); py_value = PyList_GET_ITEM (values, i); key = _pygi_argument_from_object (py_key, key_type_info, item_transfer); if (PyErr_Occurred()) { goto hash_table_item_error; } value = _pygi_argument_from_object (py_value, value_type_info, item_transfer); if (PyErr_Occurred()) { _pygi_argument_release (&key, type_info, GI_TRANSFER_NOTHING, GI_DIRECTION_IN); goto hash_table_item_error; } g_hash_table_insert (hash_table, key.v_pointer, _pygi_arg_to_hash_pointer (&value, g_type_info_get_tag (value_type_info))); continue; hash_table_item_error: /* Free everything we have converted so far. */ _pygi_argument_release ( (GIArgument *) &hash_table, type_info, GI_TRANSFER_NOTHING, GI_DIRECTION_IN); hash_table = NULL; _PyGI_ERROR_PREFIX ("Item %zd: ", i); break; } arg.v_pointer = hash_table; hash_table_release: g_base_info_unref ( (GIBaseInfo *) key_type_info); g_base_info_unref ( (GIBaseInfo *) value_type_info); Py_DECREF (keys); Py_DECREF (values); break; } case GI_TYPE_TAG_ERROR: PyErr_SetString (PyExc_NotImplementedError, "error marshalling is not supported yet"); /* TODO */ break; default: g_assert_not_reached (); } return arg; }
/* 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; }
/** * _pygi_argument_to_array * @arg: The argument to convert * @array_length_policy: Closure for marshalling the array length argument when needed. * @user_data1: Generic user data passed to the array_length_policy. * @user_data2: Generic user data passed to the array_length_policy. * @type_info: The type info for @arg * @out_free_array: A return location for a gboolean that indicates whether * or not the wrapped GArray should be freed * * Make sure an array type argument is wrapped in a GArray. * * Note: This method can *not* be folded into _pygi_argument_to_object() because * arrays are special in the sense that they might require access to @args in * order to get the length. * * Returns: A GArray wrapping @arg. If @out_free_array has been set to TRUE then * free the array with g_array_free() without freeing the data members. * Otherwise don't free the array. */ GArray * _pygi_argument_to_array (GIArgument *arg, PyGIArgArrayLengthPolicy array_length_policy, void *user_data1, void *user_data2, GITypeInfo *type_info, gboolean *out_free_array) { GITypeInfo *item_type_info; gboolean is_zero_terminated; gsize item_size; gssize length; GArray *g_array; g_return_val_if_fail (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY, NULL); if (arg->v_pointer == NULL) { return NULL; } switch (g_type_info_get_array_type (type_info)) { case GI_ARRAY_TYPE_C: is_zero_terminated = g_type_info_is_zero_terminated (type_info); item_type_info = g_type_info_get_param_type (type_info, 0); item_size = _pygi_g_type_info_size (item_type_info); g_base_info_unref ( (GIBaseInfo *) item_type_info); if (is_zero_terminated) { length = g_strv_length (arg->v_pointer); } else { length = g_type_info_get_array_fixed_size (type_info); if (length < 0) { gint length_arg_pos; if (G_UNLIKELY (array_length_policy == NULL)) { g_critical ("Unable to determine array length for %p", arg->v_pointer); g_array = g_array_new (is_zero_terminated, FALSE, item_size); *out_free_array = TRUE; return g_array; } length_arg_pos = g_type_info_get_array_length (type_info); g_assert (length_arg_pos >= 0); length = array_length_policy (length_arg_pos, user_data1, user_data2); if (length < 0) { return NULL; } } } g_assert (length >= 0); g_array = g_array_new (is_zero_terminated, FALSE, item_size); g_free (g_array->data); g_array->data = arg->v_pointer; g_array->len = length; *out_free_array = TRUE; break; case GI_ARRAY_TYPE_ARRAY: case GI_ARRAY_TYPE_BYTE_ARRAY: /* Note: GByteArray is really just a GArray */ g_array = arg->v_pointer; *out_free_array = FALSE; break; case GI_ARRAY_TYPE_PTR_ARRAY: { GPtrArray *ptr_array = (GPtrArray*) arg->v_pointer; g_array = g_array_sized_new (FALSE, FALSE, sizeof(gpointer), ptr_array->len); g_array->data = (char*) ptr_array->pdata; g_array->len = ptr_array->len; *out_free_array = TRUE; break; } default: g_critical ("Unexpected array type %u", g_type_info_get_array_type (type_info)); g_array = NULL; break; } return g_array; }