1  /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2   * GObject introspection: Virtual Function implementation
       3   *
       4   * Copyright (C) 2005 Matthias Clasen
       5   * Copyright (C) 2008,2009 Red Hat, Inc.
       6   *
       7   * SPDX-License-Identifier: LGPL-2.1-or-later
       8   *
       9   * This library is free software; you can redistribute it and/or
      10   * modify it under the terms of the GNU Lesser General Public
      11   * License as published by the Free Software Foundation; either
      12   * version 2 of the License, or (at your option) any later version.
      13   *
      14   * This library is distributed in the hope that it will be useful,
      15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17   * Lesser General Public License for more details.
      18   *
      19   * You should have received a copy of the GNU Lesser General Public
      20   * License along with this library; if not, write to the
      21   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      22   * Boston, MA 02111-1307, USA.
      23   */
      24  
      25  #include "config.h"
      26  
      27  #include <string.h>
      28  
      29  #include <glib.h>
      30  
      31  #include <girepository/girepository.h>
      32  #include "gibaseinfo-private.h"
      33  #include "girepository-private.h"
      34  #include "gitypelib-internal.h"
      35  #include "givfuncinfo.h"
      36  
      37  /**
      38   * GIVFuncInfo:
      39   *
      40   * `GIVFuncInfo` represents a virtual function.
      41   *
      42   * A virtual function is a callable object that belongs to either a
      43   * [type@GIRepository.ObjectInfo] or a [type@GIRepository.InterfaceInfo].
      44   *
      45   * Since: 2.80
      46   */
      47  
      48  GIVFuncInfo *
      49  gi_base_info_find_vfunc (GIRealInfo  *rinfo,
      50                           guint32      offset,
      51                           guint        n_vfuncs,
      52                           const gchar *name)
      53  {
      54    /* FIXME hash */
      55    Header *header = (Header *)rinfo->typelib->data;
      56  
      57    for (guint i = 0; i < n_vfuncs; i++)
      58      {
      59        VFuncBlob *fblob = (VFuncBlob *)&rinfo->typelib->data[offset];
      60        const gchar *fname = (const gchar *)&rinfo->typelib->data[fblob->name];
      61  
      62        if (strcmp (name, fname) == 0)
      63          return (GIVFuncInfo *) gi_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*) rinfo,
      64                                             rinfo->typelib, offset);
      65  
      66        offset += header->vfunc_blob_size;
      67      }
      68  
      69    return NULL;
      70  }
      71  
      72  /**
      73   * gi_vfunc_info_get_flags:
      74   * @info: a #GIVFuncInfo
      75   *
      76   * Obtain the flags for this virtual function info.
      77   *
      78   * See [flags@GIRepository.VFuncInfoFlags] for more information about possible
      79   * flag values.
      80   *
      81   * Returns: the flags
      82   * Since: 2.80
      83   */
      84  GIVFuncInfoFlags
      85  gi_vfunc_info_get_flags (GIVFuncInfo *info)
      86  {
      87    GIVFuncInfoFlags flags;
      88    GIRealInfo *rinfo = (GIRealInfo *)info;
      89    VFuncBlob *blob;
      90  
      91    g_return_val_if_fail (info != NULL, 0);
      92    g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0);
      93  
      94    blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset];
      95  
      96    flags = 0;
      97  
      98    if (blob->must_chain_up)
      99      flags = flags | GI_VFUNC_MUST_CHAIN_UP;
     100  
     101    if (blob->must_be_implemented)
     102      flags = flags | GI_VFUNC_MUST_OVERRIDE;
     103  
     104    if (blob->must_not_be_implemented)
     105      flags = flags | GI_VFUNC_MUST_NOT_OVERRIDE;
     106  
     107    if (blob->throws)
     108      flags = flags | GI_VFUNC_THROWS;
     109  
     110    return flags;
     111  }
     112  
     113  /**
     114   * gi_vfunc_info_get_offset:
     115   * @info: a #GIVFuncInfo
     116   *
     117   * Obtain the offset of the function pointer in the class struct.
     118   *
     119   * The value `0xFFFF` indicates that the struct offset is unknown.
     120   *
     121   * Returns: the struct offset or `0xFFFF` if it’s unknown
     122   * Since: 2.80
     123   */
     124  guint
     125  gi_vfunc_info_get_offset (GIVFuncInfo *info)
     126  {
     127    GIRealInfo *rinfo = (GIRealInfo *)info;
     128    VFuncBlob *blob;
     129  
     130    g_return_val_if_fail (info != NULL, 0);
     131    g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0);
     132  
     133    blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset];
     134  
     135    return blob->struct_offset;
     136  }
     137  
     138  /**
     139   * gi_vfunc_info_get_signal:
     140   * @info: a #GIVFuncInfo
     141   *
     142   * Obtain the signal for the virtual function if one is set.
     143   *
     144   * The signal comes from the object or interface to which
     145   * this virtual function belongs.
     146   *
     147   * Returns: (transfer full) (nullable): the signal, or `NULL` if none is set
     148   * Since: 2.80
     149   */
     150  GISignalInfo *
     151  gi_vfunc_info_get_signal (GIVFuncInfo *info)
     152  {
     153    GIRealInfo *rinfo = (GIRealInfo *)info;
     154    VFuncBlob *blob;
     155  
     156    g_return_val_if_fail (info != NULL, 0);
     157    g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0);
     158  
     159    blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset];
     160  
     161    if (blob->class_closure)
     162      return gi_interface_info_get_signal ((GIInterfaceInfo *)rinfo->container, blob->signal);
     163  
     164    return NULL;
     165  }
     166  
     167  /**
     168   * gi_vfunc_info_get_invoker:
     169   * @info: a #GIVFuncInfo
     170   *
     171   * If this virtual function has an associated invoker method, this
     172   * method will return it.  An invoker method is a C entry point.
     173   *
     174   * Not all virtuals will have invokers.
     175   *
     176   * Returns: (transfer full) (nullable): The [type@GIRepository.FunctionInfo] or
     177   *   `NULL` if none is set. Free it with [method@GIRepository.BaseInfo.unref]
     178   *   when done.
     179   * Since: 2.80
     180   */
     181  GIFunctionInfo *
     182  gi_vfunc_info_get_invoker (GIVFuncInfo *info)
     183  {
     184    GIRealInfo *rinfo = (GIRealInfo *)info;
     185    VFuncBlob *blob;
     186    GIBaseInfo *container;
     187    GIInfoType parent_type;
     188  
     189    g_return_val_if_fail (info != NULL, 0);
     190    g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0);
     191  
     192    blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset];
     193  
     194    /* 1023 = 0x3ff is the maximum of the 10 bits for invoker index */
     195    if (blob->invoker == 1023)
     196      return NULL;
     197  
     198    container = rinfo->container;
     199    parent_type = gi_base_info_get_info_type (container);
     200    if (parent_type == GI_INFO_TYPE_OBJECT)
     201      return gi_object_info_get_method ((GIObjectInfo*)container, blob->invoker);
     202    else if (parent_type == GI_INFO_TYPE_INTERFACE)
     203      return gi_interface_info_get_method ((GIInterfaceInfo*)container, blob->invoker);
     204    else
     205      g_assert_not_reached ();
     206  }
     207  
     208  /**
     209   * gi_vfunc_info_get_address:
     210   * @info: a #GIVFuncInfo
     211   * @implementor_gtype: [type@GObject.Type] implementing this virtual function
     212   * @error: return location for a [type@GLib.Error], or `NULL`
     213   *
     214   * Looks up where the implementation for @info is inside the type struct of
     215   * @implementor_gtype.
     216   *
     217   * Returns: address to a function
     218   * Since: 2.80
     219   */
     220  gpointer
     221  gi_vfunc_info_get_address (GIVFuncInfo  *vfunc_info,
     222                             GType         implementor_gtype,
     223                             GError      **error)
     224  {
     225    GIBaseInfo *container_info;
     226    GIInterfaceInfo *interface_info;
     227    GIObjectInfo *object_info;
     228    GIStructInfo *struct_info;
     229    GIFieldInfo *field_info = NULL;
     230    int length, i, offset;
     231    gpointer implementor_class, implementor_vtable;
     232    gpointer func = NULL;
     233  
     234    g_return_val_if_fail (vfunc_info != NULL, NULL);
     235    g_return_val_if_fail (GI_IS_VFUNC_INFO (vfunc_info), NULL);
     236    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     237  
     238    container_info = gi_base_info_get_container ((GIBaseInfo *) vfunc_info);
     239    if (gi_base_info_get_info_type (container_info) == GI_INFO_TYPE_OBJECT)
     240      {
     241        object_info = (GIObjectInfo*) container_info;
     242        interface_info = NULL;
     243        struct_info = gi_object_info_get_class_struct (object_info);
     244      }
     245    else
     246      {
     247        interface_info = (GIInterfaceInfo*) container_info;
     248        object_info = NULL;
     249        struct_info = gi_interface_info_get_iface_struct (interface_info);
     250      }
     251  
     252    length = gi_struct_info_get_n_fields (struct_info);
     253    for (i = 0; i < length; i++)
     254      {
     255        field_info = gi_struct_info_get_field (struct_info, i);
     256  
     257        if (strcmp (gi_base_info_get_name ( (GIBaseInfo*) field_info),
     258                    gi_base_info_get_name ( (GIBaseInfo*) vfunc_info)) != 0) {
     259            gi_base_info_unref ((GIBaseInfo *) field_info);
     260            field_info = NULL;
     261            continue;
     262        }
     263  
     264        break;
     265      }
     266  
     267    if (field_info == NULL)
     268      {
     269        g_set_error (error,
     270                     GI_INVOKE_ERROR,
     271                     GI_INVOKE_ERROR_SYMBOL_NOT_FOUND,
     272                     "Couldn't find struct field for this vfunc");
     273        goto out;
     274      }
     275  
     276    implementor_class = g_type_class_ref (implementor_gtype);
     277  
     278    if (object_info)
     279      {
     280        implementor_vtable = implementor_class;
     281      }
     282    else
     283      {
     284        GType interface_type;
     285  
     286        interface_type = gi_registered_type_info_get_g_type ((GIRegisteredTypeInfo*) interface_info);
     287        implementor_vtable = g_type_interface_peek (implementor_class, interface_type);
     288      }
     289  
     290    offset = gi_field_info_get_offset (field_info);
     291    func = *(gpointer*) G_STRUCT_MEMBER_P (implementor_vtable, offset);
     292    g_type_class_unref (implementor_class);
     293    gi_base_info_unref ((GIBaseInfo *) field_info);
     294  
     295    if (func == NULL)
     296      {
     297        g_set_error (error,
     298                     GI_INVOKE_ERROR,
     299                     GI_INVOKE_ERROR_SYMBOL_NOT_FOUND,
     300                     "Class %s doesn't implement %s",
     301                     g_type_name (implementor_gtype),
     302                     gi_base_info_get_name ( (GIBaseInfo*) vfunc_info));
     303        goto out;
     304      }
     305  
     306   out:
     307    gi_base_info_unref ((GIBaseInfo*) struct_info);
     308  
     309    return func;
     310  }
     311  
     312  /**
     313   * gi_vfunc_info_invoke: (skip)
     314   * @info: a #GIVFuncInfo describing the virtual function to invoke
     315   * @implementor: [type@GObject.Type] of the type that implements this virtual
     316   *   function
     317   * @in_args: (array length=n_in_args) (nullable): an array of
     318   *   [struct@GIRepository.Argument]s, one for each ‘in’ parameter of @info. If
     319   *   there are no ‘in’ parameters, @in_args can be `NULL`
     320   * @n_in_args: the length of the @in_args array
     321   * @out_args: (array length=n_out_args) (nullable): an array of
     322   *   [struct@GIRepository.Argument]s allocated by the caller, one for each
     323   *   ‘out’ parameter of @info. If there are no ‘out’ parameters, @out_args may
     324   *   be `NULL`
     325   * @n_out_args: the length of the @out_args array
     326   * @return_value: (out caller-allocates) (not optional) (nullable): return
     327   *   location for the return value from the vfunc; `NULL` may be returned if
     328   *   the vfunc returns that
     329   * @error: return location for detailed error information, or `NULL`
     330   *
     331   * Invokes the function described in @info with the given
     332   * arguments.
     333   *
     334   * Note that ‘inout’ parameters must appear in both argument lists.
     335   *
     336   * Returns: `TRUE` if the vfunc was executed successfully and didn’t throw
     337   *   a [type@GLib.Error]; `FALSE` if @error is set
     338   * Since: 2.80
     339   */
     340  gboolean
     341  gi_vfunc_info_invoke (GIVFuncInfo      *info,
     342                        GType             implementor,
     343                        const GIArgument *in_args,
     344                        gsize             n_in_args,
     345                        const GIArgument *out_args,
     346                        gsize             n_out_args,
     347                        GIArgument       *return_value,
     348                        GError          **error)
     349  {
     350    gpointer func;
     351    GError *local_error = NULL;
     352  
     353    g_return_val_if_fail (info != NULL, FALSE);
     354    g_return_val_if_fail (GI_IS_VFUNC_INFO (info), FALSE);
     355    g_return_val_if_fail (in_args != NULL || n_in_args == 0, FALSE);
     356    g_return_val_if_fail (out_args != NULL || n_out_args == 0, FALSE);
     357    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
     358  
     359    func = gi_vfunc_info_get_address (info, implementor, &local_error);
     360    if (local_error != NULL)
     361      {
     362        g_propagate_error (error, g_steal_pointer (&local_error));
     363        return FALSE;
     364      }
     365  
     366    return gi_callable_info_invoke ((GICallableInfo*) info,
     367                                    func,
     368                                    in_args,
     369                                    n_in_args,
     370                                    out_args,
     371                                    n_out_args,
     372                                    return_value,
     373                                    TRUE,
     374                                    FALSE,
     375                                    error);
     376  }
     377  
     378  void
     379  gi_vfunc_info_class_init (gpointer g_class,
     380                            gpointer class_data)
     381  {
     382    GIBaseInfoClass *info_class = g_class;
     383  
     384    info_class->info_type = GI_INFO_TYPE_VFUNC;
     385  }