Ticket UUID: | 1998273 | |||
Title: | Making a proc arg's unshared | |||
Type: | RFE | Version: | None | |
Submitter: | ferrieux | Created on: | 2008-06-19 21:55:13 | |
Subsystem: | 10. Objects | Assigned To: | msofer | |
Priority: | 5 Medium | Severity: | ||
Status: | Open | Last Modified: | 2008-06-20 14:08:56 | |
Resolution: | None | Closed By: | ||
Closed on: | ||||
Description: |
Inside a proc, a value passed as argument typically has a refcount of three. Of the two unwanted ones, one is easily removed: the local var foo $x[unset x] but the other is harder: it is in the CallFrame's objv, basically [info level 0]. The attached patch provides [info zaplevel] which prematurely neutralizes the [info level 0] data for that purpose. Thus, in proc pop x { info zaplevel lrange $x[unset x] 0 end-1 } The $x value is effectively passed unshared, provided it is passed unshared by the caller. The patch works by downing the refcounts of all members of [info level 0] except the head (to keep the proc name visible, should someone call [info level 0] after [info zaplevel]), and replacing them with references to a shared empty Tcl_Obj. Hence an [info level 0] of "foo a b c" becomes "foo {} {} {}". This gives us safety in the event of callers cacheing CallFrame->objc,objv in locals/registers. | |||
User Comments: |
ferrieux added on 2008-06-20 14:08:56:
Logged In: YES user_id=496139 Originator: YES Then shall we do ::errorStack (or [info errorStack] or ...) at last ? I can give it a try if you green-light me. (Notice that as a warned user, I wouldn't blame anyone for having an incomplete ::errorStack after a zaplevel. Again like gdb and -O3). Regarding zaplevel, I'm still interested about the nasty cases you had in mind with unknown, namespaces, aliases, etc. msofer added on 2008-06-20 07:34:22: Logged In: YES user_id=148712 Originator: NO Uhh ... right, ::errorInfo is untouched. I was thinking of substituted error stacks :} msofer added on 2008-06-20 06:26:27: Logged In: YES user_id=148712 Originator: NO About stack traces: how does this look with the patch? mig@cpq:~$ tclsh % proc zap x { catch {info zaplevel} foo } proc foo {} { ouch } zap 1% % % % invalid command name "ouch" % set errorInfo invalid command name "ouch" while executing "ouch" (procedure "foo" line 2) invoked from within "foo" (procedure "zap" line 3) invoked from within "zap 1" ferrieux added on 2008-06-20 06:19:18: Logged In: YES user_id=496139 Originator: YES Thanks for the faster KFK :-) Sorry for the misguided upload. Won't do that again. I agree about all the losses regarding [info level], but these are mild losses, first because the name [info zaplevel] is pretty clear about it, and second because the in-place replacement by {} of the args does no harm to other copies (if someone has made an extra copy of CF->objv, by definition he should have upped the refcounts. This may happen in the future, but in any case won't interfere destructively with zaplevel, just mmake it useless ;-) About stack traces, what do you mean ? We both know that ::errorStack does not exist (remember ? ;-) And ::errorInfo does not contain dynamic args like [info level 0]... About debuggers, so be it. Gdb is not too happy with -O3 either ! About [unknown], I concur. Only an insane person would zaplevel inside unknown ! But would it go so far as to crash ? Any idea to stress-test it ? msofer added on 2008-06-20 05:59:01: data_type - 360894 Logged In: YES user_id=148712 Originator: NO (a) even better than '$x[unset x]' is '$x[set x {}]', as it is completely bytecompiled (unset is not) (b) I think this belongs in the FR tracker, not patches. It may even require a TIP? The implicit contract is that the caller keeps the objv valid until the callee returns, and the CallFrame keeps a copy. This patch violates the contract. I still want to explore the full consequences of this, but is is definitely NOT 100% harmless: * Stack traces cease to work properly * Debuggers may suffer the loss of [info level], especially if they rely on leave traces. * [unknown] needs a proper [info level], and there are probable more such beasts. Not sure that this breaks it, as long as [unknown] doesn't call [info zaplevel] itself * Scripts like the following, and other commands that inspect the CallFrame stack, also stop working: proc foo x { info zaplevel ... moo } proc moo args { if {![llength $args]} { set args [info level -1] } boo $args } ... ferrieux added on 2008-06-20 05:51:49: Logged In: YES user_id=496139 Originator: YES Correction: the K-free K idea and name were given by Don, in the comments of 1890831. ferrieux added on 2008-06-20 05:32:53: Logged In: YES user_id=496139 Originator: YES No. [take x] is exactly the same as $x[unset x], but in less efficient ;-) To witness that you still have an extra ref: % proc K {x y} { set x } % proc take {v} { upvar 1 $v x ; K $x [unset x] } % proc pop x {lrange [::tcl::unsupported::debugobj [take x]] 0 end-1} % pop [list d f g h] Obj:0x9F2340(2) (list):0x9F1000-0x0 <<<?>>> d f g Now with [info zaplevel]: % proc pop x {info zaplevel;lrange [::tcl::unsupported::debugobj [take x]] 0 end-1} % pop [list d f g h] Obj:0x9DB658(1) (list):0x9EF4C8-0x0 <<<?>>> d f g PS: A side note: the $x[unset x] idiom is the K-free K given to me by Kevin IIRC. It is very efficient because the concatenation of the empty string has a shortcut and causes no duplication nor shimmering. andreas_kupries added on 2008-06-20 05:05:24: Logged In: YES user_id=75003 Originator: NO Does the following do the same? proc K {x y} { set x } proc take {v} { upvar 1 $v x ; K $x [unset x] } proc pop x { lrange [take x] 0 end-1 } ferrieux added on 2008-06-20 04:55:14: File Added - 281914: zaplevel.patch |
Attachments:
- zaplevel.patch [download] added by ferrieux on 2008-06-20 04:55:14. [details]