[perl-glib-object-introspection] Copy item memory in flat arrays when we are given ownership



commit 62d8c54328d6503d2daacbb8ddd83a9ba703e569
Author: Torsten Schönfeld <kaffeetisch gmx de>
Date:   Sat Sep 28 19:55:28 2019 +0200

    Copy item memory in flat arrays when we are given ownership
    
    The item memory in flat arrays is usually allocated en-bloc, so we
    cannot simply assume ownership of each individual array element, as that
    would lead to deallocation attempts of sub-blocks of the big
    allocation.  So instead we copy the individual items and free the array
    memory en-bloc afterwards.
    
    This happened for poppler_annot_text_markup_get_quadrilaterals.
    
    https://rt.cpan.org/Public/Bug/Display.html?id=130280
    https://gitlab.gnome.org/GNOME/perl-glib-object-introspection/issues/1

 GObjectIntrospection.xs        | 13 +++++++++++-
 gperl-i11n-field.c             |  2 ++
 gperl-i11n-invoke-c.c          |  2 ++
 gperl-i11n-invoke-perl.c       |  6 +++++-
 gperl-i11n-marshal-arg.c       |  3 ++-
 gperl-i11n-marshal-array.c     | 38 +++++++++++++++++----------------
 gperl-i11n-marshal-hash.c      | 12 +++++++++--
 gperl-i11n-marshal-interface.c | 16 ++++++++++++--
 gperl-i11n-marshal-list.c      |  6 +++++-
 t/arrays.t                     | 48 +++++++++++++++++++++++++++++++++++++++++-
 10 files changed, 119 insertions(+), 27 deletions(-)
---
diff --git a/GObjectIntrospection.xs b/GObjectIntrospection.xs
index 58fe26f..aca33f8 100644
--- a/GObjectIntrospection.xs
+++ b/GObjectIntrospection.xs
@@ -160,6 +160,11 @@ typedef struct {
        GPerlI11nInvocationInfo base;
 } GPerlI11nPerlInvocationInfo;
 
+typedef enum {
+       GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
+       GPERL_I11N_MEMORY_SCOPE_TEMPORARY,
+} GPerlI11nMemoryScope;
+
 /* callbacks */
 static GPerlI11nPerlCallbackInfo * create_perl_callback_closure_for_named_sub (GIBaseInfo *cb_info, gchar 
*sub_name);
 static GPerlI11nPerlCallbackInfo * create_perl_callback_closure (GIBaseInfo *cb_info, SV *code);
@@ -220,6 +225,7 @@ static gboolean is_forbidden_sub_name (const gchar *name);
 static SV * interface_to_sv (GITypeInfo* info,
                              GIArgument *arg,
                              gboolean own,
+                             GPerlI11nMemoryScope mem_scope,
                              GPerlI11nInvocationInfo *iinfo);
 static void sv_to_interface (GIArgInfo * arg_info,
                              GITypeInfo * type_info,
@@ -242,6 +248,7 @@ static void sv_to_arg (SV * sv,
 static SV * arg_to_sv (GIArgument * arg,
                        GITypeInfo * info,
                        GITransfer transfer,
+                       GPerlI11nMemoryScope mem_scope,
                        GPerlI11nInvocationInfo *iinfo);
 
 static gpointer sv_to_callback (GIArgInfo * arg_info, GITypeInfo * type_info, SV * sv, 
GPerlI11nInvocationInfo * invocation_info);
@@ -592,7 +599,11 @@ _fetch_constant (class, basename, constant)
        /* FIXME: What am I suppossed to do with the return value? */
        g_constant_info_get_value (info, &value);
        /* No PUTBACK/SPAGAIN needed here. */
-       RETVAL = arg_to_sv (&value, type_info, GI_TRANSFER_NOTHING, NULL);
+       RETVAL = arg_to_sv (&value,
+                           type_info,
+                           GI_TRANSFER_NOTHING,
+                           GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
+                           NULL);
 #if GI_CHECK_VERSION (1, 30, 1)
        g_constant_info_free_value (info, &value);
 #endif
diff --git a/gperl-i11n-field.c b/gperl-i11n-field.c
index e43c731..372f8ef 100644
--- a/gperl-i11n-field.c
+++ b/gperl-i11n-field.c
@@ -78,6 +78,7 @@ get_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer)
                sv = arg_to_sv (&value,
                                field_type,
                                GI_TRANSFER_NOTHING,
+                               GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
                                NULL);
        }
 
@@ -97,6 +98,7 @@ get_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer)
                sv = arg_to_sv (&value,
                                field_type,
                                transfer,
+                               GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
                                NULL);
        }
 
diff --git a/gperl-i11n-invoke-c.c b/gperl-i11n-invoke-c.c
index a6d7dac..01d60f8 100644
--- a/gperl-i11n-invoke-c.c
+++ b/gperl-i11n-invoke-c.c
@@ -236,6 +236,7 @@ invoke_c_code (GICallableInfo *info,
                value = SAVED_STACK_SV (arg_to_sv (&return_value,
                                                   &iinfo.base.return_type_info,
                                                   iinfo.base.return_type_transfer,
+                                                  GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
                                                   &iinfo.base));
                if (value) {
                        XPUSHs (sv_2mortal (value));
@@ -268,6 +269,7 @@ invoke_c_code (GICallableInfo *info,
                        sv = SAVED_STACK_SV (arg_to_sv (iinfo.out_args[i].v_pointer,
                                                        &(iinfo.base.arg_types[i]),
                                                        transfer,
+                                                       GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
                                                        &iinfo.base));
                        if (sv) {
                                XPUSHs (sv_2mortal (sv));
diff --git a/gperl-i11n-invoke-perl.c b/gperl-i11n-invoke-perl.c
index 5dc338e..104716c 100644
--- a/gperl-i11n-invoke-perl.c
+++ b/gperl-i11n-invoke-perl.c
@@ -112,7 +112,11 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
                                ? *((gpointer *) args[i+args_offset])
                                : args[i+args_offset];
                        raw_to_arg (raw, &arg, arg_type);
-                       sv = SAVED_STACK_SV (arg_to_sv (&arg, arg_type, transfer, &iinfo.base));
+                       sv = SAVED_STACK_SV (arg_to_sv (&arg,
+                                                       arg_type,
+                                                       transfer,
+                                                       GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
+                                                       &iinfo.base));
                        /* If arg_to_sv returns NULL, we take that as 'skip
                         * this argument'; happens for GDestroyNotify, for
                         * example. */
diff --git a/gperl-i11n-marshal-arg.c b/gperl-i11n-marshal-arg.c
index e9d7f0a..8c27250 100644
--- a/gperl-i11n-marshal-arg.c
+++ b/gperl-i11n-marshal-arg.c
@@ -150,6 +150,7 @@ static SV *
 arg_to_sv (GIArgument * arg,
            GITypeInfo * info,
            GITransfer transfer,
+           GPerlI11nMemoryScope mem_scope,
            GPerlI11nInvocationInfo *iinfo)
 {
        GITypeTag tag = g_type_info_get_tag (info);
@@ -230,7 +231,7 @@ arg_to_sv (GIArgument * arg,
                return array_to_sv (info, arg->v_pointer, transfer, iinfo);
 
            case GI_TYPE_TAG_INTERFACE:
-               return interface_to_sv (info, arg, own, iinfo);
+               return interface_to_sv (info, arg, own, mem_scope, iinfo);
 
            case GI_TYPE_TAG_GLIST:
            case GI_TYPE_TAG_GSLIST:
diff --git a/gperl-i11n-marshal-array.c b/gperl-i11n-marshal-array.c
index ab6fa71..3e2274d 100644
--- a/gperl-i11n-marshal-array.c
+++ b/gperl-i11n-marshal-array.c
@@ -75,6 +75,8 @@ array_to_sv (GITypeInfo *info,
        GITypeInfo *param_info;
        GITypeTag param_tag;
        gsize item_size;
+       GITransfer item_transfer;
+       gboolean free_element_data;
        gboolean need_struct_value_semantics;
        gssize length = -1, i;
        AV *av;
@@ -106,7 +108,9 @@ array_to_sv (GITypeInfo *info,
                                g_assert (iinfo && iinfo->aux_args);
                                conversion_sv = arg_to_sv (&(iinfo->aux_args[length_pos]),
                                                           &(iinfo->arg_types[length_pos]),
-                                                          GI_TRANSFER_NOTHING, NULL);
+                                                          GI_TRANSFER_NOTHING,
+                                                          GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
+                                                          NULL);
                                length = SvIV (conversion_sv);
                                SvREFCNT_dec (conversion_sv);
                        }
@@ -135,6 +139,13 @@ array_to_sv (GITypeInfo *info,
        param_tag = g_type_info_get_tag (param_info);
        item_size = size_of_type_info (param_info);
 
+       /* FIXME: What about an array containing arrays of strings, where the
+        * outer array is GI_TRANSFER_EVERYTHING but the inner arrays are
+        * GI_TRANSFER_CONTAINER? */
+       item_transfer = transfer == GI_TRANSFER_EVERYTHING
+               ? GI_TRANSFER_EVERYTHING
+               : GI_TRANSFER_NOTHING;
+
        av = newAV ();
 
        need_struct_value_semantics =
@@ -152,26 +163,17 @@ array_to_sv (GITypeInfo *info,
 
        for (i = 0; i < length; i++) {
                GIArgument arg;
-               SV *value;
+               SV *value = NULL;
                gpointer element = elements + ((gsize) i) * item_size;
-               GITransfer item_transfer;
-               dwarn ("  element %"G_GSSIZE_FORMAT": %p\n", i, element);
+               gpointer raw_pointer = element;
+               GPerlI11nMemoryScope mem_scope = GPERL_I11N_MEMORY_SCOPE_IRRELEVANT;
                if (need_struct_value_semantics) {
-                       /* With struct value semantics, the values are freed
-                        * further below when the array itself is freed, so we
-                        * must not free the elements here. */
-                       item_transfer = GI_TRANSFER_NOTHING;
-                       raw_to_arg (&element, &arg, param_info);
-               } else {
-                       /* FIXME: What about an array containing arrays of strings, where the
-                        * outer array is GI_TRANSFER_EVERYTHING but the inner arrays are
-                        * GI_TRANSFER_CONTAINER? */
-                       item_transfer = transfer == GI_TRANSFER_EVERYTHING
-                               ? GI_TRANSFER_EVERYTHING
-                               : GI_TRANSFER_NOTHING;
-                       raw_to_arg (element, &arg, param_info);
+                       raw_pointer = &element;
+                       mem_scope = GPERL_I11N_MEMORY_SCOPE_TEMPORARY;
                }
-               value = arg_to_sv (&arg, param_info, item_transfer, iinfo);
+               dwarn ("  element %"G_GSSIZE_FORMAT": %p, pointer: %p\n", i, element, raw_pointer);
+               raw_to_arg (raw_pointer, &arg, param_info);
+               value = arg_to_sv (&arg, param_info, item_transfer, mem_scope, iinfo);
                if (value)
                        av_push (av, value);
        }
diff --git a/gperl-i11n-marshal-hash.c b/gperl-i11n-marshal-hash.c
index 2c5f0e8..db9809c 100644
--- a/gperl-i11n-marshal-hash.c
+++ b/gperl-i11n-marshal-hash.c
@@ -47,13 +47,21 @@ ghash_to_sv (GITypeInfo *info,
 
                dwarn ("  key pointer %p\n", key_p);
                arg.v_pointer = key_p;
-               key_sv = arg_to_sv (&arg, key_param_info, item_transfer, NULL);
+               key_sv = arg_to_sv (&arg,
+                                   key_param_info,
+                                   item_transfer,
+                                   GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
+                                   NULL);
                if (key_sv == NULL)
                         break;
 
                dwarn ("  value pointer %p\n", value_p);
                arg.v_pointer = value_p;
-               value_sv = arg_to_sv (&arg, value_param_info, item_transfer, NULL);
+               value_sv = arg_to_sv (&arg,
+                                     value_param_info,
+                                     item_transfer,
+                                     GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
+                                     NULL);
                if (value_sv == NULL)
                        break;
 
diff --git a/gperl-i11n-marshal-interface.c b/gperl-i11n-marshal-interface.c
index b81e0bf..9715209 100644
--- a/gperl-i11n-marshal-interface.c
+++ b/gperl-i11n-marshal-interface.c
@@ -317,13 +317,18 @@ sv_to_interface (GIArgInfo * arg_info,
  * struct_to_sv), so it needs to be wrapped with PUTBACK/SPAGAIN by the
  * caller. */
 static SV *
-interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own, GPerlI11nInvocationInfo *iinfo)
+interface_to_sv (GITypeInfo* info,
+                 GIArgument *arg,
+                 gboolean own,
+                 GPerlI11nMemoryScope mem_scope,
+                 GPerlI11nInvocationInfo *iinfo)
 {
        GIBaseInfo *interface;
        GIInfoType info_type;
        SV *sv = NULL;
 
        dwarn ("arg %p, info %p\n", arg, info);
+       dwarn ("  is pointer: %d\n", g_type_info_is_pointer (info));
 
        interface = g_type_info_get_interface (info);
        if (!interface)
@@ -382,7 +387,14 @@ interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own, GPerlI11nInvoc
                else if (g_type_is_a (type, G_TYPE_BOXED)) {
                        dwarn ("  -> boxed: pointer=%p, type=%"G_GSIZE_FORMAT" (%s), own=%d\n",
                               arg->v_pointer, type, g_type_name (type), own);
-                       sv = gperl_new_boxed (arg->v_pointer, type, own);
+                       switch (mem_scope) {
+                           case GPERL_I11N_MEMORY_SCOPE_TEMPORARY:
+                               g_assert (own == TRUE);
+                               sv = gperl_new_boxed_copy (arg->v_pointer, type);
+                               break;
+                           default:
+                               sv = gperl_new_boxed (arg->v_pointer, type, own);
+                       }
                }
 
 #if GLIB_CHECK_VERSION (2, 24, 0)
diff --git a/gperl-i11n-marshal-list.c b/gperl-i11n-marshal-list.c
index 41c370d..a392e71 100644
--- a/gperl-i11n-marshal-list.c
+++ b/gperl-i11n-marshal-list.c
@@ -53,7 +53,11 @@ glist_to_sv (GITypeInfo* info,
                GIArgument arg = {0,};
                dwarn ("  element %p: %p\n", i, i->data);
                arg.v_pointer = i->data;
-               value = arg_to_sv (&arg, param_info, item_transfer, NULL);
+               value = arg_to_sv (&arg,
+                                  param_info,
+                                  item_transfer,
+                                  GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
+                                  NULL);
                if (value)
                        av_push (av, value);
        }
diff --git a/t/arrays.t b/t/arrays.t
index f7bc1a6..5358cee 100644
--- a/t/arrays.t
+++ b/t/arrays.t
@@ -6,7 +6,7 @@ use strict;
 use warnings;
 use utf8;
 
-plan tests => 72;
+plan tests => 88;
 
 ok (Regress::test_strv_in ([ '1', '2', '3' ]));
 
@@ -179,3 +179,49 @@ SKIP: {
   }, 'user23');
   $obj->emit_sig_with_array_len_prop ();
 }
+
+# -----------------------------------------------------------------------------
+
+SKIP: {
+  my $have_poppler = eval {
+    Glib::Object::Introspection->setup (
+      basename    => 'Poppler',
+      version     => '0.18',
+      package     => 'Poppler');
+    1;
+  };
+  skip 'flat array tests using Poppler', 1
+    unless $have_poppler;
+
+  my $pdf = <<__PDF__; # https://github.com/mathiasbynens/small/blob/master/pdf.pdf
+%PDF-1.
+1 0 obj<</Pages 2 0 R>>endobj
+2 0 obj<</Kids[3 0 R]/Count 1>>endobj
+3 0 obj<</Parent 2 0 R>>endobj
+trailer <</Root 1 0 R>>
+__PDF__
+
+  my $doc = Poppler::Document->new_from_data ($pdf, length $pdf, undef);
+  my $quads = [
+    Glib::Boxed::new ('Poppler::Quadrilateral',
+                      {p1 => {x => 0, y => 0},
+                       p2 => {x => 1, y => 1},
+                       p3 => {x => 2, y => 2},
+                       p4 => {x => 3, y => 3}}),
+    Glib::Boxed::new ('Poppler::Quadrilateral',
+                      {p1 => {x => 4, y => 4},
+                       p2 => {x => 5, y => 5},
+                       p3 => {x => 6, y => 6},
+                       p4 => {x => 7, y => 7}}),
+  ];
+  my $rect = Glib::Boxed::new ('Poppler::Rectangle', {x1 => 0, y1 => 0, x2 => 9, y2 => 9});
+  my $annot = Poppler::AnnotTextMarkup->new_highlight ($doc, $rect, $quads);
+  my $new_quads = $annot->get_quadrilaterals ();
+  for my $index (0 .. 1) {
+    for my $point (qw/p1 p2 p3 p4/) {
+      for my $coord (qw/x y/) {
+        is ($new_quads->[$index]->$point->$coord, $quads->[$index]->$point->$coord);
+      }
+    }
+  }
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]