static GIFieldInfo * get_field_info (JSContext *context, Boxed *priv, jsid id) { int field_index; jsval id_val; if (!JS_IdToValue(context, id, &id_val)) return JS_FALSE; if (!JSVAL_IS_INT (id_val)) { gjs_throw(context, "Field index for %s is not an integer", g_base_info_get_name ((GIBaseInfo *)priv->info)); return NULL; } field_index = JSVAL_TO_INT(id_val); if (field_index < 0 || field_index >= g_struct_info_get_n_fields (priv->info)) { gjs_throw(context, "Bad field index %d for %s", field_index, g_base_info_get_name ((GIBaseInfo *)priv->info)); return NULL; } return g_struct_info_get_field (priv->info, field_index); }
static PyObject * _get_fields (PyGIBaseInfo *self, GIInfoType info_type) { gssize n_infos; PyObject *infos; gssize i; switch (info_type) { case GI_INFO_TYPE_STRUCT: n_infos = g_struct_info_get_n_fields ( (GIStructInfo *) self->info); break; case GI_INFO_TYPE_OBJECT: n_infos = g_object_info_get_n_fields ( (GIObjectInfo *) self->info); break; default: g_assert_not_reached(); } infos = PyTuple_New (n_infos); if (infos == NULL) { return NULL; } for (i = 0; i < n_infos; i++) { GIBaseInfo *info; PyObject *py_info; switch (info_type) { case GI_INFO_TYPE_STRUCT: info = (GIBaseInfo *) g_struct_info_get_field ( (GIStructInfo *) self->info, i); break; case GI_INFO_TYPE_OBJECT: info = (GIBaseInfo *) g_object_info_get_field ( (GIObjectInfo *) self->info, i); break; default: g_assert_not_reached(); } g_assert (info != NULL); py_info = _pygi_info_new (info); g_base_info_unref (info); if (py_info == NULL) { Py_CLEAR (infos); break; } PyTuple_SET_ITEM (infos, i, py_info); } return infos; }
static JSBool define_boxed_class_fields (JSContext *context, Boxed *priv, JSObject *proto) { int n_fields = g_struct_info_get_n_fields (priv->info); int i; /* We identify properties with a 'TinyId': a 8-bit numeric value * that can be retrieved in the property getter/setter. Using it * allows us to avoid a hash-table lookup or linear search. * It does restrict us to a maximum of 256 fields per type. * * We define all fields as read/write so that the user gets an * error message. If we omitted fields or defined them read-only * we'd: * * - Storing a new property for a non-accessible field * - Silently do nothing when writing a read-only field * * Which is pretty confusing if the only reason a field isn't * writable is language binding or memory-management restrictions. * * We just go ahead and define the fields immediately for the * class; doing it lazily in boxed_new_resolve() would be possible * as well if doing it ahead of time caused to much start-up * memory overhead. */ if (n_fields > 256) { gjs_debug(GJS_DEBUG_ERROR, "Only defining the first 256 fields in boxed type '%s'", g_base_info_get_name ((GIBaseInfo *)priv->info)); n_fields = 256; } for (i = 0; i < n_fields; i++) { GIFieldInfo *field = g_struct_info_get_field (priv->info, i); const char *field_name = g_base_info_get_name ((GIBaseInfo *)field); gboolean result; result = JS_DefinePropertyWithTinyId(context, proto, field_name, i, JSVAL_NULL, boxed_field_getter, boxed_field_setter, JSPROP_PERMANENT | JSPROP_SHARED); g_base_info_unref ((GIBaseInfo *)field); if (!result) return JS_FALSE; } return JS_TRUE; }
/* When initializing a boxed object from a hash of properties, we don't want * to do n O(n) lookups, so put temporarily put the fields into a hash table * for fast lookup. We could also do this ahead of time and store it on proto->priv. */ static GHashTable * get_field_map(GIStructInfo *struct_info) { GHashTable *result; int n_fields; int i; result = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_base_info_unref); n_fields = g_struct_info_get_n_fields(struct_info); for (i = 0; i < n_fields; i++) { GIFieldInfo *field_info = g_struct_info_get_field(struct_info, i); g_hash_table_insert(result, (char *)g_base_info_get_name((GIBaseInfo *)field_info), field_info); } return result; }
/* Check if the type of the boxed is "simple" - every field is a non-pointer * type that we know how to assign to. If so, then we can allocate and free * instances without needing a constructor. */ static gboolean struct_is_simple(GIStructInfo *info) { int n_fields = g_struct_info_get_n_fields(info); gboolean is_simple = TRUE; int i; /* If it's opaque, it's not simple */ if (n_fields == 0) return FALSE; for (i = 0; i < n_fields && is_simple; i++) { GIFieldInfo *field_info = g_struct_info_get_field(info, i); GITypeInfo *type_info = g_field_info_get_type(field_info); is_simple = type_can_be_allocated_directly(type_info); g_base_info_unref((GIBaseInfo *)field_info); g_base_info_unref((GIBaseInfo *)type_info); } return is_simple; }
static void create_native_closure (GType interface_type, GIInterfaceInfo *iface_info, GIVFuncInfo *vfunc_info, MethodImpl *impl) { GIFunctionInfo *invoker_info; GIStructInfo *struct_info; GIFieldInfo *field_info; GITypeInfo *type_info; GICallbackInfo *callback_info; guint n_fields, i; gboolean found_field_info; invoker_info = g_vfunc_info_get_invoker (vfunc_info); if (invoker_info == NULL) { g_debug ("No invoker for VFunc '%s.%s'", g_base_info_get_name (iface_info), g_base_info_get_name (vfunc_info)); return; } struct_info = g_interface_info_get_iface_struct (iface_info); n_fields = g_struct_info_get_n_fields (struct_info); found_field_info = FALSE; for (i = 0; i < n_fields; i++) { field_info = g_struct_info_get_field (struct_info, i); if (strcmp (g_base_info_get_name (field_info), g_base_info_get_name (vfunc_info)) == 0) { found_field_info = TRUE; break; } g_base_info_unref (field_info); } if (!found_field_info) { g_debug ("No struct field for VFunc '%s.%s'", g_base_info_get_name (iface_info), g_base_info_get_name (vfunc_info)); g_base_info_unref (struct_info); g_base_info_unref (invoker_info); return; } type_info = g_field_info_get_type (field_info); g_assert (g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE); callback_info = g_type_info_get_interface (type_info); g_assert (g_base_info_get_type (callback_info) == GI_INFO_TYPE_CALLBACK); impl->interface_type = interface_type; impl->invoker_info = invoker_info; impl->method_name = g_base_info_get_name (invoker_info); impl->closure = g_callable_info_prepare_closure (callback_info, &impl->cif, handle_method_impl, impl); impl->struct_offset = g_field_info_get_offset (field_info); g_base_info_unref (callback_info); g_base_info_unref (type_info); g_base_info_unref (field_info); g_base_info_unref (struct_info); }
gboolean pygi_g_struct_info_is_simple (GIStructInfo *struct_info) { gboolean is_simple; gsize n_field_infos; gsize i; is_simple = TRUE; n_field_infos = g_struct_info_get_n_fields (struct_info); for (i = 0; i < n_field_infos && is_simple; i++) { GIFieldInfo *field_info; GITypeInfo *field_type_info; GITypeTag field_type_tag; field_info = g_struct_info_get_field (struct_info, i); field_type_info = g_field_info_get_type (field_info); field_type_tag = g_type_info_get_tag (field_type_info); switch (field_type_tag) { case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_UNICHAR: if (g_type_info_is_pointer (field_type_info)) { is_simple = FALSE; } break; case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_ERROR: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: is_simple = FALSE; break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info; GIInfoType info_type; info = g_type_info_get_interface (field_type_info); info_type = g_base_info_get_type (info); switch (info_type) { case GI_INFO_TYPE_STRUCT: if (g_type_info_is_pointer (field_type_info)) { is_simple = FALSE; } else { is_simple = pygi_g_struct_info_is_simple ( (GIStructInfo *) info); } break; case GI_INFO_TYPE_UNION: /* TODO */ is_simple = FALSE; break; case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: if (g_type_info_is_pointer (field_type_info)) { is_simple = FALSE; } break; case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_CALLBACK: case GI_INFO_TYPE_INTERFACE: is_simple = FALSE; break; case GI_INFO_TYPE_VFUNC: case GI_INFO_TYPE_INVALID: case GI_INFO_TYPE_FUNCTION: case GI_INFO_TYPE_CONSTANT: case GI_INFO_TYPE_VALUE: case GI_INFO_TYPE_SIGNAL: case GI_INFO_TYPE_PROPERTY: case GI_INFO_TYPE_FIELD: case GI_INFO_TYPE_ARG: case GI_INFO_TYPE_TYPE: case GI_INFO_TYPE_UNRESOLVED: default: g_assert_not_reached(); break; } g_base_info_unref (info); break; } } g_base_info_unref ( (GIBaseInfo *) field_type_info); g_base_info_unref ( (GIBaseInfo *) field_info); } return is_simple; }
static gpointer sv_to_struct (GITransfer transfer, GIBaseInfo * info, GIInfoType info_type, SV * sv) { HV *hv; gsize size = 0; GITransfer field_transfer; gpointer pointer = NULL; dwarn ("sv = %p\n", sv); if (!gperl_sv_is_defined (sv)) return NULL; if (is_struct_disguised (info)) { gchar *package; dwarn (" disguised struct\n"); package = get_struct_package (info); g_assert (package); if (!gperl_sv_is_ref (sv) || !sv_derived_from (sv, package)) ccroak ("Cannot convert scalar %p to an object of type %s", sv, package); g_free (package); return INT2PTR (void *, SvIV ((SV *) SvRV (sv))); } if (!gperl_sv_is_hash_ref (sv)) ccroak ("need a hash ref to convert to struct of type %s", g_base_info_get_name (info)); hv = (HV *) SvRV (sv); switch (info_type) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: size = g_struct_info_get_size ((GIStructInfo *) info); break; case GI_INFO_TYPE_UNION: size = g_union_info_get_size ((GIStructInfo *) info); break; default: g_assert_not_reached (); } dwarn (" size = %"G_GSIZE_FORMAT"\n", size); field_transfer = GI_TRANSFER_NOTHING; dwarn (" transfer = %d\n", transfer); switch (transfer) { case GI_TRANSFER_EVERYTHING: field_transfer = GI_TRANSFER_EVERYTHING; /* fall through */ case GI_TRANSFER_CONTAINER: /* FIXME: What if there's a special allocator for the record? * Like GSlice? */ pointer = g_malloc0 (size); break; default: pointer = gperl_alloc_temp (size); break; } switch (info_type) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: { gint i, n_fields = g_struct_info_get_n_fields ((GIStructInfo *) info); for (i = 0; i < n_fields; i++) { GIFieldInfo *field_info; const gchar *field_name; SV **svp; field_info = g_struct_info_get_field ( (GIStructInfo *) info, i); /* FIXME: Check GIFieldInfoFlags. */ field_name = g_base_info_get_name ( (GIBaseInfo *) field_info); dwarn (" field %d (%s)\n", i, field_name); svp = hv_fetch (hv, field_name, strlen (field_name), 0); if (svp && gperl_sv_is_defined (*svp)) { set_field (field_info, pointer, field_transfer, *svp); } g_base_info_unref ((GIBaseInfo *) field_info); } break; } case GI_INFO_TYPE_UNION: ccroak ("%s: unions not handled yet", G_STRFUNC); default: ccroak ("%s: unhandled info type %d", G_STRFUNC, info_type); } return pointer; }
/* This may call Perl code (via get_field), so it needs to be wrapped with * PUTBACK/SPAGAIN by the caller. */ static SV * struct_to_sv (GIBaseInfo* info, GIInfoType info_type, gpointer pointer, gboolean own) { HV *hv; dwarn ("pointer = %p\n", pointer); if (pointer == NULL) { return &PL_sv_undef; } if (is_struct_disguised (info)) { SV *sv; gchar *package; dwarn (" disguised struct\n"); g_assert (!own); package = get_struct_package (info); g_assert (package); sv = newSV (0); sv_setref_pv (sv, package, pointer); g_free (package); return sv; } hv = newHV (); switch (info_type) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: { gint i, n_fields = g_struct_info_get_n_fields ((GIStructInfo *) info); for (i = 0; i < n_fields; i++) { GIFieldInfo *field_info; SV *sv; field_info = g_struct_info_get_field ((GIStructInfo *) info, i); dwarn (" field %d (%s)\n", i, g_base_info_get_name (field_info)); /* FIXME: Check GIFieldInfoFlags. */ /* FIXME: Is it right to use GI_TRANSFER_NOTHING * here? */ sv = get_field (field_info, pointer, GI_TRANSFER_NOTHING); if (gperl_sv_is_defined (sv)) { const gchar *name; name = g_base_info_get_name ( (GIBaseInfo *) field_info); gperl_hv_take_sv (hv, name, strlen (name), sv); } g_base_info_unref ((GIBaseInfo *) field_info); } break; } case GI_INFO_TYPE_UNION: ccroak ("%s: unions not handled yet", G_STRFUNC); default: ccroak ("%s: unhandled info type %d", G_STRFUNC, info_type); } if (own) { /* FIXME: Is it correct to just call g_free here? What if the * thing was allocated via GSlice? */ g_free (pointer); } return newRV_noinc ((SV *) hv); }
/* FIXME: Should g-i offer API for this? */ static gboolean is_struct_disguised (GIBaseInfo* info) { return 0 == g_struct_info_get_n_fields (info) && 0 == g_struct_info_get_size (info); }