(root)/
glib-2.79.0/
girepository/
giroffsets.c
       1  /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2   * GObject introspection: Compute structure offsets
       3   *
       4   * Copyright (C) 2008 Red Hat, Inc.
       5   *
       6   * SPDX-License-Identifier: LGPL-2.1-or-later
       7   *
       8   * This library is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU Lesser General Public
      10   * License as published by the Free Software Foundation; either
      11   * version 2 of the License, or (at your option) any later version.
      12   *
      13   * This library is distributed in the hope that it will be useful,
      14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16   * Lesser General Public License for more details.
      17   *
      18   * You should have received a copy of the GNU Lesser General Public
      19   * License along with this library; if not, write to the
      20   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      21   * Boston, MA 02111-1307, USA.
      22   */
      23  
      24  #include "girffi.h"
      25  
      26  #include "girnode-private.h"
      27  
      28  #include <string.h>
      29  
      30  /* The C standard specifies that an enumeration can be any char or any signed
      31   * or unsigned integer type capable of representing all the values of the
      32   * enumeration. We use test enumerations to figure out what choices the
      33   * compiler makes. (Ignoring > 32 bit enumerations)
      34   */
      35  
      36  typedef enum {
      37    ENUM_1 = 1 /* compiler could use int8, uint8, int16, uint16, int32, uint32 */
      38  } Enum1;
      39  
      40  typedef enum {
      41    ENUM_2 = 128 /* compiler could use uint8, int16, uint16, int32, uint32 */
      42  } Enum2;
      43  
      44  typedef enum {
      45    ENUM_3 = 257 /* compiler could use int16, uint16, int32, uint32 */
      46  } Enum3;
      47  
      48  typedef enum {
      49    ENUM_4 = G_MAXSHORT + 1 /* compiler could use uint16, int32, uint32 */
      50  } Enum4;
      51  
      52  typedef enum {
      53    ENUM_5 = G_MAXUSHORT + 1 /* compiler could use int32, uint32 */
      54  } Enum5;
      55  
      56  typedef enum {
      57    ENUM_6 = ((guint)G_MAXINT) + 1 /* compiler could use uint32 */
      58  } Enum6;
      59  
      60  typedef enum {
      61    ENUM_7 = -1 /* compiler could use int8, int16, int32 */
      62  } Enum7;
      63  
      64  typedef enum {
      65    ENUM_8 = -129 /* compiler could use int16, int32 */
      66  } Enum8;
      67  
      68  typedef enum {
      69    ENUM_9 = G_MINSHORT - 1 /* compiler could use int32 */
      70  } Enum9;
      71  
      72  static void
      73  compute_enum_storage_type (GIIrNodeEnum *enum_node)
      74  {
      75    GList *l;
      76    gint64 max_value = 0;
      77    gint64 min_value = 0;
      78    int width;
      79    gboolean signed_type;
      80  
      81    if (enum_node->storage_type != GI_TYPE_TAG_VOID) /* already done */
      82      return;
      83  
      84    for (l = enum_node->values; l; l = l->next)
      85      {
      86        GIIrNodeValue *value = l->data;
      87        if (value->value > max_value)
      88          max_value = value->value;
      89        if (value->value < min_value)
      90          min_value = value->value;
      91      }
      92  
      93    if (min_value < 0)
      94      {
      95        signed_type = TRUE;
      96  
      97        if (min_value > -128 && max_value <= 127)
      98  	width = sizeof(Enum7);
      99        else if (min_value >= G_MINSHORT && max_value <= G_MAXSHORT)
     100  	width = sizeof(Enum8);
     101        else
     102  	width = sizeof(Enum9);
     103      }
     104    else
     105      {
     106        if (max_value <= 127)
     107  	{
     108  	  width = sizeof (Enum1);
     109  	  signed_type = (gint64)(Enum1)(-1) < 0;
     110  	}
     111        else if (max_value <= 255)
     112  	{
     113  	  width = sizeof (Enum2);
     114  	  signed_type = (gint64)(Enum2)(-1) < 0;
     115  	}
     116        else if (max_value <= G_MAXSHORT)
     117  	{
     118  	  width = sizeof (Enum3);
     119  	  signed_type = (gint64)(Enum3)(-1) < 0;
     120  	}
     121        else if (max_value <= G_MAXUSHORT)
     122  	{
     123  	  width = sizeof (Enum4);
     124  	  signed_type = (gint64)(Enum4)(-1) < 0;
     125  	}
     126        else if (max_value <= G_MAXINT)
     127  	{
     128  	  width = sizeof (Enum5);
     129  	  signed_type = (gint64)(Enum5)(-1) < 0;
     130  	}
     131        else
     132  	{
     133  	  width = sizeof (Enum6);
     134  	  signed_type = (gint64)(Enum6)(-1) < 0;
     135  	}
     136      }
     137  
     138    if (width == 1)
     139      enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT8 : GI_TYPE_TAG_UINT8;
     140    else if (width == 2)
     141      enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT16 : GI_TYPE_TAG_UINT16;
     142    else if (width == 4)
     143      enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT32 : GI_TYPE_TAG_UINT32;
     144    else if (width == 8)
     145      enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT64 : GI_TYPE_TAG_UINT64;
     146    else
     147      g_error ("Unexpected enum width %d", width);
     148  }
     149  
     150  static gboolean
     151  get_enum_size_alignment (GIIrNodeEnum *enum_node,
     152                           gint         *size,
     153                           gint         *alignment)
     154  {
     155    ffi_type *type_ffi;
     156  
     157    compute_enum_storage_type (enum_node);
     158  
     159    switch (enum_node->storage_type)
     160      {
     161      case GI_TYPE_TAG_INT8:
     162      case GI_TYPE_TAG_UINT8:
     163        type_ffi = &ffi_type_uint8;
     164        break;
     165      case GI_TYPE_TAG_INT16:
     166      case GI_TYPE_TAG_UINT16:
     167        type_ffi = &ffi_type_uint16;
     168        break;
     169      case GI_TYPE_TAG_INT32:
     170      case GI_TYPE_TAG_UINT32:
     171        type_ffi = &ffi_type_uint32;
     172        break;
     173      case GI_TYPE_TAG_INT64:
     174      case GI_TYPE_TAG_UINT64:
     175        type_ffi = &ffi_type_uint64;
     176        break;
     177      default:
     178        g_error ("Unexpected enum storage type %s",
     179  	       gi_type_tag_to_string (enum_node->storage_type));
     180      }
     181  
     182    *size = type_ffi->size;
     183    *alignment = type_ffi->alignment;
     184  
     185    return TRUE;
     186  }
     187  
     188  static gboolean
     189  get_interface_size_alignment (GIIrTypelibBuild *build,
     190                                GIIrNodeType     *type,
     191                                gint             *size,
     192                                gint             *alignment,
     193                                const char       *who)
     194  {
     195    GIIrNode *iface;
     196  
     197    iface = gi_ir_find_node (build, ((GIIrNode*)type)->module, type->giinterface);
     198    if (!iface)
     199      {
     200        gi_ir_module_fatal (build, 0, "Can't resolve type '%s' for %s", type->giinterface, who);
     201        *size = -1;
     202        *alignment = -1;
     203        return FALSE;
     204      }
     205  
     206    gi_ir_node_compute_offsets (build, iface);
     207  
     208    switch (iface->type)
     209      {
     210      case GI_IR_NODE_BOXED:
     211        {
     212  	GIIrNodeBoxed *boxed = (GIIrNodeBoxed *)iface;
     213  	*size = boxed->size;
     214  	*alignment = boxed->alignment;
     215  	break;
     216        }
     217      case GI_IR_NODE_STRUCT:
     218        {
     219  	GIIrNodeStruct *struct_ = (GIIrNodeStruct *)iface;
     220  	*size = struct_->size;
     221  	*alignment = struct_->alignment;
     222  	break;
     223        }
     224      case GI_IR_NODE_OBJECT:
     225      case GI_IR_NODE_INTERFACE:
     226        {
     227  	GIIrNodeInterface *interface = (GIIrNodeInterface *)iface;
     228  	*size = interface->size;
     229  	*alignment = interface->alignment;
     230  	break;
     231        }
     232      case GI_IR_NODE_UNION:
     233        {
     234  	GIIrNodeUnion *union_ = (GIIrNodeUnion *)iface;
     235  	*size = union_->size;
     236  	*alignment = union_->alignment;
     237  	break;
     238        }
     239      case GI_IR_NODE_ENUM:
     240      case GI_IR_NODE_FLAGS:
     241        {
     242  	return get_enum_size_alignment ((GIIrNodeEnum *)iface,
     243  					size, alignment);
     244        }
     245      case GI_IR_NODE_CALLBACK:
     246        {
     247  	*size = ffi_type_pointer.size;
     248  	*alignment = ffi_type_pointer.alignment;
     249  	break;
     250        }
     251      default:
     252        {
     253  	g_warning ("%s has is not a pointer and is of type %s",
     254                     who,
     255  		   gi_ir_node_type_to_string (iface->type));
     256  	*size = -1;
     257  	*alignment = -1;
     258  	break;
     259        }
     260      }
     261  
     262    return *alignment > 0;
     263  }
     264  
     265  static gboolean
     266  get_type_size_alignment (GIIrTypelibBuild *build,
     267                           GIIrNodeType     *type,
     268                           gint             *size,
     269                           gint             *alignment,
     270                           const char       *who)
     271  {
     272    ffi_type *type_ffi;
     273  
     274    if (type->is_pointer)
     275      {
     276        type_ffi = &ffi_type_pointer;
     277      }
     278    else if (type->tag == GI_TYPE_TAG_ARRAY)
     279      {
     280        gint elt_size, elt_alignment;
     281  
     282        if (!type->has_size
     283            || !get_type_size_alignment(build, type->parameter_type1,
     284                                        &elt_size, &elt_alignment, who))
     285          {
     286            *size = -1;
     287            *alignment = -1;
     288            return FALSE;
     289          }
     290  
     291        *size = type->size * elt_size;
     292        *alignment = elt_alignment;
     293  
     294        return TRUE;
     295      }
     296    else
     297      {
     298        if (type->tag == GI_TYPE_TAG_INTERFACE)
     299  	{
     300  	  return get_interface_size_alignment (build, type, size, alignment, who);
     301  	}
     302        else
     303  	{
     304  	  type_ffi = gi_type_tag_get_ffi_type (type->tag, type->is_pointer);
     305  
     306  	  if (type_ffi == &ffi_type_void)
     307  	    {
     308  	      g_warning ("%s has void type", who);
     309  	      *size = -1;
     310  	      *alignment = -1;
     311  	      return FALSE;
     312  	    }
     313  	  else if (type_ffi == &ffi_type_pointer)
     314  	    {
     315  	      g_warning ("%s has is not a pointer and is of type %s",
     316                           who,
     317  			 gi_type_tag_to_string (type->tag));
     318  	      *size = -1;
     319  	      *alignment = -1;
     320  	      return FALSE;
     321  	    }
     322  	}
     323      }
     324  
     325    g_assert (type_ffi);
     326    *size = type_ffi->size;
     327    *alignment = type_ffi->alignment;
     328  
     329    return TRUE;
     330  }
     331  
     332  static gboolean
     333  get_field_size_alignment (GIIrTypelibBuild *build,
     334                            GIIrNodeField    *field,
     335                            GIIrNode         *parent_node,
     336                            gint             *size,
     337                            gint             *alignment)
     338  {
     339    GIIrModule *module = build->module;
     340    gchar *who;
     341    gboolean success;
     342  
     343    who = g_strdup_printf ("field %s.%s.%s", module->name, parent_node->name, ((GIIrNode *)field)->name);
     344  
     345    if (field->callback)
     346      {
     347        *size = ffi_type_pointer.size;
     348        *alignment = ffi_type_pointer.alignment;
     349        success = TRUE;
     350      }
     351    else
     352      success = get_type_size_alignment (build, field->type, size, alignment, who);
     353    g_free (who);
     354  
     355    return success;
     356  }
     357  
     358  #define GI_ALIGN(n, align) (((n) + (align) - 1) & ~((align) - 1))
     359  
     360  static gboolean
     361  compute_struct_field_offsets (GIIrTypelibBuild *build,
     362                                GIIrNode         *node,
     363                                GList            *members,
     364                                gint             *size_out,
     365                                gint             *alignment_out)
     366  {
     367    int size = 0;
     368    int alignment = 1;
     369    GList *l;
     370    gboolean have_error = FALSE;
     371  
     372    *alignment_out = -2; /* mark to detect recursion */
     373  
     374    for (l = members; l; l = l->next)
     375      {
     376        GIIrNode *member = (GIIrNode *)l->data;
     377  
     378        if (member->type == GI_IR_NODE_FIELD)
     379  	{
     380  	  GIIrNodeField *field = (GIIrNodeField *)member;
     381  
     382  	  if (!have_error)
     383  	    {
     384  	      int member_size;
     385  	      int member_alignment;
     386  
     387  	      if (get_field_size_alignment (build, field, node,
     388  					    &member_size, &member_alignment))
     389  		{
     390  		  size = GI_ALIGN (size, member_alignment);
     391  		  alignment = MAX (alignment, member_alignment);
     392  		  field->offset = size;
     393  		  size += member_size;
     394  		}
     395  	      else
     396  		have_error = TRUE;
     397  	    }
     398  
     399  	  if (have_error)
     400  	    field->offset = -1;
     401  	}
     402        else if (member->type == GI_IR_NODE_CALLBACK)
     403  	{
     404            size = GI_ALIGN (size, ffi_type_pointer.alignment);
     405            alignment = MAX (alignment, ffi_type_pointer.alignment);
     406  	  size += ffi_type_pointer.size;
     407  	}
     408      }
     409  
     410    /* Structs are tail-padded out to a multiple of their alignment */
     411    size = GI_ALIGN (size, alignment);
     412  
     413    if (!have_error)
     414      {
     415        *size_out = size;
     416        *alignment_out = alignment;
     417      }
     418    else
     419      {
     420        *size_out = -1;
     421        *alignment_out = -1;
     422      }
     423  
     424    return !have_error;
     425  }
     426  
     427  static gboolean
     428  compute_union_field_offsets (GIIrTypelibBuild *build,
     429                               GIIrNode         *node,
     430                               GList            *members,
     431                               gint             *size_out,
     432                               gint             *alignment_out)
     433  {
     434    int size = 0;
     435    int alignment = 1;
     436    GList *l;
     437    gboolean have_error = FALSE;
     438  
     439    *alignment_out = -2; /* mark to detect recursion */
     440  
     441    for (l = members; l; l = l->next)
     442      {
     443        GIIrNode *member = (GIIrNode *)l->data;
     444  
     445        if (member->type == GI_IR_NODE_FIELD)
     446  	{
     447  	  GIIrNodeField *field = (GIIrNodeField *)member;
     448  
     449  	  if (!have_error)
     450  	    {
     451  	      int member_size;
     452  	      int member_alignment;
     453  
     454  	      if (get_field_size_alignment (build,field, node,
     455  					    &member_size, &member_alignment))
     456  		{
     457  		  size = MAX (size, member_size);
     458  		  alignment = MAX (alignment, member_alignment);
     459  		}
     460  	      else
     461  		have_error = TRUE;
     462  	    }
     463  	}
     464      }
     465  
     466    /* Unions are tail-padded out to a multiple of their alignment */
     467    size = GI_ALIGN (size, alignment);
     468  
     469    if (!have_error)
     470      {
     471        *size_out = size;
     472        *alignment_out = alignment;
     473      }
     474    else
     475      {
     476        *size_out = -1;
     477        *alignment_out = -1;
     478      }
     479  
     480    return !have_error;
     481  }
     482  
     483  static gboolean
     484  check_needs_computation (GIIrTypelibBuild *build,
     485                           GIIrNode         *node,
     486                           gint              alignment)
     487  {
     488    GIIrModule *module = build->module;
     489    /*
     490     *  0: Not yet computed
     491     * >0: Previously succeeded
     492     * -1: Previously failed
     493     * -2: In progress
     494     */
     495    if (alignment == -2)
     496      {
     497        g_warning ("Recursion encountered when computing the size of %s.%s",
     498  		 module->name, node->name);
     499      }
     500  
     501    return alignment == 0;
     502  }
     503  
     504  /*
     505   * gi_ir_node_compute_offsets:
     506   * @build: Current typelib build
     507   * @node: a #GIIrNode
     508   *
     509   * If a node is a a structure or union, makes sure that the field
     510   * offsets have been computed, and also computes the overall size and
     511   * alignment for the type.
     512   *
     513   * Since: 2.80
     514   */
     515  void
     516  gi_ir_node_compute_offsets (GIIrTypelibBuild *build,
     517                              GIIrNode         *node)
     518  {
     519    gboolean appended_stack;
     520  
     521    if (build->stack)
     522      appended_stack = node != (GIIrNode*)build->stack->data;
     523    else
     524      appended_stack = TRUE;
     525    if (appended_stack)
     526      build->stack = g_list_prepend (build->stack, node);
     527  
     528    switch (node->type)
     529      {
     530      case GI_IR_NODE_BOXED:
     531        {
     532  	GIIrNodeBoxed *boxed = (GIIrNodeBoxed *)node;
     533  
     534  	if (!check_needs_computation (build, node, boxed->alignment))
     535  	  return;
     536  
     537  	compute_struct_field_offsets (build, node, boxed->members,
     538  				      &boxed->size, &boxed->alignment);
     539  	break;
     540        }
     541      case GI_IR_NODE_STRUCT:
     542        {
     543  	GIIrNodeStruct *struct_ = (GIIrNodeStruct *)node;
     544  
     545  	if (!check_needs_computation (build, node, struct_->alignment))
     546  	  return;
     547  
     548  	compute_struct_field_offsets (build, node, struct_->members,
     549  				      &struct_->size, &struct_->alignment);
     550  	break;
     551        }
     552      case GI_IR_NODE_OBJECT:
     553      case GI_IR_NODE_INTERFACE:
     554        {
     555  	GIIrNodeInterface *iface = (GIIrNodeInterface *)node;
     556  
     557  	if (!check_needs_computation (build, node, iface->alignment))
     558  	  return;
     559  
     560  	compute_struct_field_offsets (build, node, iface->members,
     561  				      &iface->size, &iface->alignment);
     562  	break;
     563        }
     564      case GI_IR_NODE_UNION:
     565        {
     566  	GIIrNodeUnion *union_ = (GIIrNodeUnion *)node;
     567  
     568  	if (!check_needs_computation (build, node, union_->alignment))
     569  	  return;
     570  
     571  	compute_union_field_offsets (build, (GIIrNode*)union_, union_->members,
     572  				     &union_->size, &union_->alignment);
     573  	break;
     574        }
     575      case GI_IR_NODE_ENUM:
     576      case GI_IR_NODE_FLAGS:
     577        {
     578  	GIIrNodeEnum *enum_ = (GIIrNodeEnum *)node;
     579  
     580  	if (enum_->storage_type != GI_TYPE_TAG_VOID) /* already done */
     581  	  return;
     582  
     583  	compute_enum_storage_type (enum_);
     584  
     585  	break;
     586        }
     587      default:
     588        break;
     589      }
     590    
     591    if (appended_stack)
     592      build->stack = g_list_delete_link (build->stack, build->stack);
     593  }