Re: custom model rows-reordered marshal



Kevin Ryde wrote:
There might be another similar bit in gtk_tree_model_get too.

OK, I just sat down and I think I figured it out.  If you are *really*
interested, I attach two files: perl-stack-fiddling lists the expansions of the
relevant macros[1], and perl-stack-fiddling-tree-model-get-all is a "trace" of
what happens to the relevant stacks[2] at each point in the execution of
$model->get($iter).

It all boils down to this: when Perl code is invoked, for example by
gtk2perl_tree_model_get_value, the arguments for the Perl code are put on the
stack *after the element pointed to by the global stack pointer*.  That is,
everything after the global stack pointer is the scratch area used by the
calling code to marshal arguments in and out of Perl code.  So when you put
stuff on the stack in some XSUB, and there's a chance that some code is going to
invoke Perl code before you return from the XSUB, make sure you update the
global stack pointer to point to the element you just put on the stack.

In practical terms: after every XPUSHs or similar, use PUTBACK if it's possible
that some Perl code is invoked before you return from the XSUB.

I hope I can remember all this because I don't want to have to think through it
again.

The case where you give explicit column numbers seems ok.  I suppose the
sp global is already past where the local XPUSHs is writing.  But do
both cases need an SPAGAIN in case the stack has been extended deep
within the get_value and/or get_n_columns?

In practice, Gtk2::TreeModel::get with explicit column numbers is safe since
there are always more input arguments than return values, and so the stuff we
put on the stack can't end up being overwritten.  But a PUTBACK can't hurt
either, I think.  The get_n_columns() call is harmless since we haven't put
anything on the stack yet that might be overwritten.  So, I think the attached
patch should do it.

Or for that matter the iter_n_children in the marshal too?

I don't see how iter_n_children is affected by this -- can you elaborate?

Does all of the above make sense?

-- 
Bye,
-Torsten

[1] Created with Porting/expand-macro.pl from bleadperl.
[2] Actually, I think the markstack stuff isn't relevant after all.  So maybe
just ignore that.
dSP
        SV **sp = (my_perl->Istack_sp)

dXSARGS
        SV **sp = (my_perl->Istack_sp);
        I32 ax = (*(my_perl->Imarkstack_ptr)--);
        SV **mark = (my_perl->Istack_base) + ax++;
        I32 items = (I32) (sp - mark)

PUSHMARK(A0)
        (void) ({
                if (++(my_perl->Imarkstack_ptr) == (my_perl->Imarkstack_max))
                        Perl_markstack_grow (my_perl);
                *(my_perl->Imarkstack_ptr) = (I32) ((A0) - (my_perl->Istack_base));
        })

ST(A0)
        (my_perl->Istack_base)[ax + (A0)]

PUSHs(A0)
        (*++sp = (A0))

XPUSHs(A0)
        (void) ({
                (void) ({
                        if ((my_perl->Istack_max) - sp < (int) (1)) {
                                sp = Perl_stack_grow (my_perl, sp, sp, (int) (1));
                        }
                });
                (*++sp = (A0));
        })

POPs
        (*sp--)

PUTBACK
        (my_perl->Istack_sp) = sp

SPAGAIN
        sp = (my_perl->Istack_sp)
my @a = $m->get($i)

XS(XS_Gtk2__TreeModel_get)

  markstack: ... | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
                   ^sp
                   ^PL_stack_sp

dXSARGS -> PL_markstack_ptr--

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
                   ^sp
                   ^PL_stack_sp

PPCODE -> sp -= items

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
           ^sp
                   ^PL_stack_sp

gtk_tree_model_get_n_columns (tree_model)

gtk2perl_tree_model_get_n_columns

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
                   ^sp
                   ^PL_stack_sp

PUSHMARK (SP)

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
                   ^y
                   ^sp
                   ^PL_stack_sp

PUSHs (m)

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | m | ...
           ^x
                   ^y
                       ^sp
                   ^PL_stack_sp

PUTBACK

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | m | ...
           ^x
                   ^y
                       ^sp
                       ^PL_stack_sp

call_method("GET_N_COLUMNS") -> GET_N_COLUMNS(m) -> returns n

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | n | ...
           ^x
                   ^y
                       ^sp
                       ^PL_stack_sp

SPAGAIN

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | n | ...
           ^x
                   ^y
                       ^sp
                       ^PL_stack_sp

POPi

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i |   | ...
           ^x
                   ^y
                   ^sp
                       ^PL_stack_sp

PUTBACK

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i |   | ...
           ^x
                   ^y
                   ^sp
                   ^PL_stack_sp

back in XS(XS_Gtk2__TreeModel_get)

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
                   ^y
           ^sp
                   ^PL_stack_sp

gtk_tree_model_get_value (tree_model, iter, 0, &gvalue)

gtk2perl_tree_model_get_value (tree_model, iter, 0, &gvalue)

  markstack: ... | y | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
                   ^y
                   ^sp
                   ^PL_stack_sp

PUSHMARK (SP)

  markstack: ... | z | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
                   ^z
                   ^sp
                   ^PL_stack_sp

PUSHs (m), PUSHs (i), PUSHs (0)

  markstack: ... | z | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | m | i | 0 | ...
           ^x
                   ^z
                               ^sp
                   ^PL_stack_sp

PUTBACK

  markstack: ... | z | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | m | i | 0 | ...
           ^x
                   ^z
                               ^sp
                               ^PL_stack_sp

call_method("GET_VALUE") -> GET_VALUE(m, i, 0) -> returns v

  markstack: ... | z | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | v |   |   | ...
           ^x
                   ^z
                               ^sp
                       ^PL_stack_sp

SPAGAIN

  markstack: ... | z | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i | v |   |   | ...
           ^x
                   ^z
                       ^sp
                       ^PL_stack_sp

POPs

  markstack: ... | z | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i |   |   |   | ...
           ^x
                   ^z
                   ^sp
                       ^PL_stack_sp

PUTBACK

  markstack: ... | z | x | ...
                   ^PL_markstack_ptr

  stack: ... | m | i |   |   |   | ...
           ^x
                   ^z
                   ^sp
                   ^PL_stack_sp

back in XS(XS_Gtk2__TreeModel_get)

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | m | i | ...
           ^x
           ^sp
                   ^PL_stack_sp
XPUSHs(v)

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | v | i | ...
           ^x
               ^sp
                   ^PL_stack_sp

in the next iteration, after the call to gtk_tree_model_get_value returned:

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | v | i | ...
           ^x
               ^sp
                   ^PL_stack_sp
XPUSHs(v)

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | v | v | ...
           ^x
                   ^sp
                   ^PL_stack_sp

in the next iteration, after the call to gtk_tree_model_get_value returned:

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | v | v | ...
           ^x
                   ^sp
                   ^PL_stack_sp
XPUSHs(v)

  markstack: ... | x | ...
               ^PL_markstack_ptr

  stack: ... | v | v | v | ...
           ^x
                       ^sp
                   ^PL_stack_sp

When we now call into gtk_tree_model_get_value, the shit hits the fan.  The
marshaling code overwrite everything beyond PL_stack_sp and so nukes everything
after and including the third value we pushed on the stack.
Index: xs/GtkTreeModel.xs
===================================================================
RCS file: /cvsroot/gtk2-perl/gtk2-perl-xs/Gtk2/xs/GtkTreeModel.xs,v
retrieving revision 1.57
diff -u -d -p -r1.57 GtkTreeModel.xs
--- xs/GtkTreeModel.xs  18 Aug 2008 20:28:44 -0000      1.57
+++ xs/GtkTreeModel.xs  24 Aug 2008 17:10:46 -0000
@@ -1132,26 +1132,29 @@ gtk_tree_model_get (tree_model, iter, ..
        int i;
     PPCODE:
        PERL_UNUSED_VAR (ix);
-       /* if column id's were passed, just return those columns */
-       if( items > 2 )
-       {
+       if (items > 2) {
+               /* if column id's were passed, just return those columns */
                for (i = 2 ; i < items ; i++) {
                        GValue gvalue = {0, };
                        gtk_tree_model_get_value (tree_model, iter,
                                                  SvIV (ST (i)), &gvalue);
                        XPUSHs (sv_2mortal (gperl_sv_from_value (&gvalue)));
+                       // Update the global stack pointer so that other xsubs
+                       // don't overwrite what we put on the stack.
+                       PUTBACK;
                        g_value_unset (&gvalue);
                }
-       }
-       else
-       {
+       } else {
                /* otherwise return all of the columns */
-               for( i = 0; i < gtk_tree_model_get_n_columns(tree_model); i++ )
-               {
+               int n_columns = gtk_tree_model_get_n_columns (tree_model);
+               for (i = 0; i < n_columns; i++) {
                        GValue gvalue = {0, };
                        gtk_tree_model_get_value (tree_model, iter,
                                                  i, &gvalue);
                        XPUSHs (sv_2mortal (gperl_sv_from_value (&gvalue)));
+                       // Update the global stack pointer so that other xsubs
+                       // don't overwrite what we put on the stack.
+                       PUTBACK;
                        g_value_unset (&gvalue);
                }
        }


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