|
From: Donal K. F. <don...@ma...> - 2017-12-03 09:16:06
|
I'm pleased to say that my branch with making Tcl arrays be first-class objects seems to be working. It's not finished yet (there's a suspicious number of "FIXME"s still in the code) and is neither cleaned up nor documented, but is passing our current tests. (I've not yet done a performance comparison with trunk.) Better yet, as the arrays are real Tcl arrays when in the callframe — and the cost of moving them in and out isn't silly like it would be with dicts (i.e., it is shuffling pointers instead of iterating over the whole dict/array) — it will support [upvar] and access from standard Tcl commands much better. At the quadcode level, this is all done through the introduction of a new type bit, ARRAY, which indicates that the value is an array. When mixed with a STRING, it means that the value is either an array or a string. It also mixes correctly with flags like NEXIST, FAIL or CALLFRAME. To go with this, there's a slew of new quadcodes, including: arrayExists — is the value an array? throwIfNotArray — throw if value doesn't support array ops throwNotArray — throw, value definitely known to not support array ops throwIfArray — throw if value does support array ops throwIsArray — throw, value definitely known to support array ops extractScalar — get the non-array component extractArray — get the array component initArray — create a new array value initArrayIfNotExists — create array if arg was NEXIST, else pass arrayElementExists - is element present in array arrayGet — read element from array (yields NEXIST STRING) arraySet — write element to array, yielding updated ARRAY arrayUnset — remove element from array, yielding updated ARRAY I may have missed some: this is ongoing work after all. :-) Higher-level operations like incrementing, lappending, etc. are described by composing these other basic operations. The pattern of handling conditional throws and so on is modelled after the way we tackle NEXISTs. At the implementation level, an ARRAY is a reference type. It is implemented as a pointer to a struct of type ARRAYIMPL, which contains three elements: the pointer to the TclVarHashTable (standard Tcl internal type), a reference count (I hypothesise that this never gets larger than 2 and then only momentarily) and a "provenance" bit, that gets set when the TclVarHashTable is linked from a callframe and so is not safe to delete directly. As we can't put arrays inside arrays and always give array variables names, the reference count handling is trivial in practice and operations that transform an array can actually just update it and return it. There's also an ARRAYSTRING which is a simple tagged "union" of STRING and ARRAY. I also had to add another new quadcode, setReturnCode, to get some of our error handling right (it's issued where an INST_BEGIN_CATCH4 would be). It's got no result at all, and its sole side effect is to reset the current Tcl result code to zero. Without it, I was getting weird effects from [try/finally] in a loop with caught failures. I'll backport that stuff to trunk along with a few fixes to exception handling implementations. Donal. |