Tony Mechelynck wrote:

> On 17/12/08 13:34, Bram Moolenaar wrote:
> >
> > Dominique Pelle wrote:
> >
> >> 2008/12/16 Bram Moolenaar<b...@moolenaar.net>:
> >>>
> >>> Matt Wozniski wrote:
> >>>
> >>>> function! ReturnArgs(...)
> >>>>    return a:000
> >>>> endfunction
> >>>>
> >>>> " Seems to work fine?
> >>>> echo ReturnArgs(1, 2, 3)
> >>>>
> >>>> " SEGV
> >>>> echo string(ReturnArgs(1, 2, 3))
> >>>>
> >>>> function! MakeArgsDict(...)
> >>>>    return { 'args': a:000 }
> >>>> endfunction
> >>>>
> >>>> " E685 Internal Error
> >>>> echo MakeArgsDict(1, 2, 3)
> >>>>
> >>>> " SEGV
> >>>> echo string(MakeArgsDict(1, 2, 3))
> >>> For it crashes a while after trying these things.  Most likely the
> >>> reference count for a:000 is wrong.  Never thought of someone returning
> >>> it...
> >> When I debugged, I found that v_list was pointing to an invalid address,
> >> which had been set in call_user_func() to&fc.l_varlist;  This variable is
> >> in the stack and was only valid while in call_user_func() and the 
> >> functions it
> >> may calls.  Somehow, a list still refers to this address after returning 
> >> from
> >> call_user_func() so v_list points then to an invalid address.
> >>
> >> Making variable fc static (in function call_user_func()) avoids using an
> >> invalid address and thus avoids a crash, but it's still not the right way
> >> to fix it.
> >>
> >>> " Seems to work fine?
> >>> echo ReturnArgs(1, 2, 3)
> >> Actually, even though this appears to work, valgrind memory checker
> >> already sees a problem there:
> >>
> >> ==23275== Invalid read of size 4
> >> ==23275==    at 0x809C577: echo_string (eval.c:7232)
> >> ==23275==    by 0x80AD48C: ex_echo (eval.c:19481)
> >> ==23275==    by 0x80C71C6: do_one_cmd (ex_docmd.c:2622)
> >> ==23275==    by 0x80C4A46: do_cmdline (ex_docmd.c:1096)
> >> ==23275==    by 0x8149D7A: nv_colon (normal.c:5233)
> >> ==23275==    by 0x81433FE: normal_cmd (normal.c:1200)
> >> ==23275==    by 0x810678D: main_loop (main.c:1180)
> >> ==23275==    by 0x81062DA: main (main.c:939)
> >> ==23275==  Address 0xbef5e280 is not stack'd, malloc'd or (recently) free'd
> >> ==23275==
> >> ==23275== Invalid write of size 4
> >> ==23275==    at 0x809C59D: echo_string (eval.c:7239)
> >> ==23275==    by 0x80AD48C: ex_echo (eval.c:19481)
> >> ==23275==    by 0x80C71C6: do_one_cmd (ex_docmd.c:2622)
> >> ==23275==    by 0x80C4A46: do_cmdline (ex_docmd.c:1096)
> >> ==23275==    by 0x8149D7A: nv_colon (normal.c:5233)
> >> ==23275==    by 0x81433FE: normal_cmd (normal.c:1200)
> >> ==23275==    by 0x810678D: main_loop (main.c:1180)
> >> ==23275==    by 0x81062DA: main (main.c:939)
> >> ==23275==  Address 0xbef5e280 is not stack'd, malloc'd or (recently) free'd
> >>
> >> (etc, more errors to follow)
> >>
> >> Line eval.c:7232 is:
> >>
> >>   7232             else if (copyID != 0&&  tv->vval.v_list->lv_copyID == 
> >> copyID)
> >>   7233             {
> >>   7234                 *tofree = NULL;
> >>   7235                 r = (char_u *)"[...]";
> >>   7236             }
> >>
> >> 'tv->vval.v_list' points to the invalid address which set as there
> >> at line 21193:
> >>
> >> 21191     v->di_tv.v_type = VAR_LIST;
> >> 21192     v->di_tv.v_lock = VAR_FIXED;
> >> 21193     v->di_tv.vval.v_list =&fc.l_varlist;
> >>
> >> (fc being a local var in the stack)
> >
> > What is happening here is that a few things are put on the stack to
> > avoid malloc()/free() calls.  These are quite expensive and adds
> > overhead to every function call.
> >
> > The reference count of these are not used.  When the function returns,
> > the items automatically disappear.  That's a bit of a problem if you
> > return the value or assigned it to a global variable.
> >
> > I think the only proper solution is to do that malloc()/free().
> > Not only for a:000, but also for l: and a:.  And all elements
> > contained in them, that's going to be time consuming.
> >
> > Another method would be to disallow passing these dictionaries to
> > outside the function scope.  One would have to make a copy instead.
> > Checking for this may be complicated though.  And this also doesn't
> > solve the problem for variables such as a:firstline that are also on the
> > stack.
> >
> > It looks like the best solution is to put the whole funccall_T in
> > allocated memory.  And only free it when all the reference counts are
> > back to zero.  Need to make a list of them and add some code to the
> > garbage collector.
> 
> When returning a Number (e.g. ":return a:lastline") or a Float, I 
> suppose the return method guarantees that there'll be no problem. I'm 
> less certain about returning a String variable local to the function, 
> but I suppose existing code already provides for that. A Funcref to a 
> function defined inside the function should remain valid -- or does it? 
> But what we're talking about here is when the returned object is a List 
> or Dictionary containing (at any level of depth) something local to the 
> function, which would disappear at return-time. (Returning a pointer to 
> a _global_ List or Dictionary ought to be no problem if no local 
> elements or sub-elements have been added by the function.)

This only matters for a:000, l: and a:.  These are allocated on the
stack.  All other things are in allocated memory and can be passed
around without problems.

> Proposed solution: returning a List or Dictionary local to the function 
> is forbidden (and generates an error). But what about returning a List 
> or Dictionary containing _elements_ local to the function? Maybe 
> nondestructively check (recursively, but breaking out on first error).
> 
> The user can of course catch the error by wrapping the return statement 
> in a try block (if he wants to), triggering a deepcopy() to a global 
> variable if caught.
> 
> Alternative solution: returning a List or Dictionary actually returns a 
> global deepcopy(). But this would impair performance, and it might have 
> other unobvious consequences.

We could do something special for the return statement.  But assigning
the things to a global variable is the most difficult issue.  Checking
each assignment is a bit of a problem.  I can't be 100% sure that we
will find all places in the code that need to have this "function local
thing that needs to be copied" check.  And it will make all assignments
a bit slower.

Allocating the whole funccall_T once and freeing it later is not that
much overhead.  It's the number of allocations that matter, not the
size.  The only tricky thing is checking that there is a reference to
the items inside.  That's something not too difficult to figure out and
easy to check if it works.

-- 
VOICE OVER: As the horrendous Black Beast lunged forward, escape for Arthur
            and his knights seemed hopeless,  when, suddenly ... the animator
            suffered a fatal heart attack.
ANIMATOR:   Aaaaagh!
VOICE OVER: The cartoon peril was no more ... The Quest for Holy Grail could
            continue.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\        download, build and distribute -- http://www.A-A-P.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Raspunde prin e-mail lui