- Fixes a bug, by removing dummy nodes together with the node
itself. (The previous implementation could leave dummy nodes in frames
which otherwise had no tbc variables, and therefore would not close
variables; that could leave 'tbclist' pointing higher than 'top', which
could dangle if the stack shrank.)
- Computes MAXDELTA based on the type of delta, to ease changing its
type if needed.
- Instead of 'isdummy', uses 'delta==0' to signal dummy nodes. (Dummy
nodes always have MAXDELTA for their real delta.)
To-be-closed variables are linked in their own list, embedded into the
stack elements. (Due to alignment, this information does not change
the size of the stack elements in most architectures.) This new list
does not produce garbage and avoids memory errors when creating tbc
variables.
Initial implementation to allow yields inside '__close' metamethods.
This current version still does not allow a '__close' metamethod
to yield when called due to an error. '__close' metamethods from
C functions also are not allowed to yield.
A to-be-closed variable is constant and it must have a close metamethod
when it is created. A program has to go out of its way (e.g., by
changing the variable's metamethod) to invalidate that check. So,
it is not worth to test that again. If the program tampers with the
metamethod, Lua will raise a regular error when attempting to call it.
Undo commit c220b0a5d0: '__close' is not called again in case of
errors. (Upvalue is removed from the list before the call.) The
common error that justified that change was C stack overflows, which
are much rarer with the stackless implementation.
- After converting a generic GCObject to a specific type ('gco2*'),
avoid using the original GCObject (to reduce aliasing).
- Small corrections in comments in 'lopcodes.h'
- Added tests about who calls __close metamethods
- Macro 'gray2black' was renamed 'nw2black' (Non-White to black), as it
was already being used on objects that could be already black.
- Macros 'white2gray' and 'black2gray' were unified in 'set2gray'; no
reason to have two macros when one will do and, again, 'black2gray' was
already being used on objects that could be already gray.
Moreover, macros 'maskcolors' and 'maskgcbits' were negated to have
ones in the described bits, instead of zeros. (This naming seems more
intuitive.)
Small changes to ensure that all objects are kept 'new' in incremental
GC (except for fixed strings, which are always old) and to make that
fact clearer.
When initializing a to-be-closed variable, check whether it has a
'__close' metamethod (or is a false value) and raise an error if
if it hasn't. This produces more accurate error messages. (The
check before closing still need to be done: in the C API, the value
is not constant; and the object may lose its '__close' metamethod
during the block.)
Open upvalues are kept alive together with their corresponding
stack. This change makes a simpler and safer fix to the issue in
commit 440a5ee78c, about upvalues in the list of open upvalues
being collected while others are being created. (That previous fix
may not be correct.)
When creating an upvalue, an emergency collection can collect the
previous upvalue where the new one would be linked. The following
code can trigger the bug, using valgrind on Lua compiled with the
-DHARDMEMTESTS option:
local x; local y
(function () return y end)();
(function () return x end)()
OP_RETURN must update trap before updating stack. (Bug detected with
-DHARDSTACKTESTS). Also, in 'luaF_close', do not create a variable
with 'uplevel(uv)', as the stack may change and invalidate this
value. (This is not a bug, but could become one if 'upl' was used
again.)
An error in a closing method may be caused by a lack of resources,
such as memory or stack space, and the error may free enough resources
(by unwinding the stack) to allow the method to work if called again.
If the closing method is already running after some error (including
its own), it is not called again.
When there are multiple errors when closing objects, the error
reported by the protected call is the first one, for two reasons:
First, other errors may be caused by this one;
second, the first error is handled in the original execution context,
and therefore has the full traceback.
Several small improvements (code style, warnings, comments, more tests),
in particular:
- 'lua_topointer' extended to handle strings
- raises an error in 'string.format("%10q")' ('%q' with modifiers)
- in the manual for 'string.format', the term "option" replaced by
"conversion specifier" (the term used by the C standard)
To-be-closed variables must contain objects with '__toclose'
metamethods (or nil). Functions were removed for several reasons:
* Functions interact badly with sandboxes. If a sandbox raises
an error to interrupt a script, a to-be-closed function still
can hijack control and continue running arbitrary sandboxed code.
* Functions interact badly with coroutines. If a coroutine yields
and is never resumed again, its to-be-closed functions will never
run. To-be-closed objects, on the other hand, will still be closed,
provided they have appropriate finalizers.
* If you really need a function, it is easy to create a dummy
object to run that function in its '__toclose' metamethod.
This comit also adds closing of variables in case of panic.
New functions to reset/kill a thread/coroutine, mainly (only?) to
close any pending to-be-closed variable. ('lua_resetthread' also
allows a thread to be reused...)
It is an error for a to-be-closed variable to have a non-closable
non-nil value when it is being closed. This situation does not seem to
be useful and often hints to an error. (Particularly in the C API, it is
easy to change a to-be-closed index by mistake.)
The mechanism of "caching the last closure created for a prototype to
try to reuse it the next time a closure for that prototype is created"
was removed. There are several reasons:
- It is hard to find a natural example where this cache has a measurable
impact on performance.
- Programmers already perceive closure creation as something slow,
so they tend to avoid it inside hot paths. (Any case where the cache
could reuse a closure can be rewritten predefining the closure in some
variable and using that variable.)
- The implementation was somewhat complex, due to a bad interaction
with the generational collector. (Typically, new closures are new,
while prototypes are old. So, the cache breaks the invariant that
old objects should not point to new ones.)
A closing method cannot be called in its own stack slot, as there may
be returning values in the stack after that slot, and the call would
corrupt those values. Instead, the closing method must be copied to the
top of the stack to be called.
Moreover, even when a function returns no value, its return istruction
still has to have its position (which will set the stack top) after
the local variables, otherwise a closing method might corrupt another
not-yet-called closing method.
values, so that the array can use bytes instead of ints, reducing
its size. (A new array 'abslineinfo' is used when line differences
do not fit in a byte.)