Update to 5.4.2

This commit is contained in:
alex 2021-03-10 20:46:07 +00:00
parent 619231f297
commit 95f31eafc0
79 changed files with 1081 additions and 838 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
project(lua LANGUAGES C VERSION 5.4.0) project(lua LANGUAGES C VERSION 5.4.2)
option(LUA_SUPPORT_DL "Support dynamic loading of compiled modules" OFF) option(LUA_SUPPORT_DL "Support dynamic loading of compiled modules" OFF)
@ -18,4 +18,4 @@ else()
endif() endif()
add_subdirectory(lua-5.4.0) add_subdirectory(lua-5.4.2)

View File

@ -43,7 +43,7 @@ if(UNIX)
find_library(LIBM m) find_library(LIBM m)
#TODO: Redo this with find_package #TODO: Redo this with find_package
if(NOT LIBM) if(NOT LIBM)
message(FATAL_ERROR "libm not found and requred by lua") message(FATAL_ERROR "libm not found and is required by lua")
endif() endif()
target_link_libraries(lua_static INTERFACE ${LIBM}) target_link_libraries(lua_static INTERFACE ${LIBM})

View File

@ -46,7 +46,7 @@ TO_MAN= lua.1 luac.1
# Lua version and release. # Lua version and release.
V= 5.4 V= 5.4
R= $V.0 R= $V.2
# Targets start here. # Targets start here.
all: $(PLAT) all: $(PLAT)

View File

@ -1,5 +1,5 @@
This is Lua 5.4.0, released on 18 Jun 2020. This is Lua 5.4.2, released on 13 Nov 2020.
For installation instructions, license details, and For installation instructions, license details, and
further information about Lua, see doc/readme.html. further information about Lua, see doc/readme.html.

View File

@ -95,6 +95,7 @@ Freely available under the terms of the
<UL> <UL>
<LI><A HREF="manual.html#4.1.1">4.1.1 &ndash; Stack Size</A> <LI><A HREF="manual.html#4.1.1">4.1.1 &ndash; Stack Size</A>
<LI><A HREF="manual.html#4.1.2">4.1.2 &ndash; Valid and Acceptable Indices</A> <LI><A HREF="manual.html#4.1.2">4.1.2 &ndash; Valid and Acceptable Indices</A>
<LI><A HREF="manual.html#4.1.3">4.1.3 &ndash; Pointers to strings</A>
</UL> </UL>
<LI><A HREF="manual.html#4.2">4.2 &ndash; C Closures</A> <LI><A HREF="manual.html#4.2">4.2 &ndash; C Closures</A>
<LI><A HREF="manual.html#4.3">4.3 &ndash; Registry</A> <LI><A HREF="manual.html#4.3">4.3 &ndash; Registry</A>
@ -197,7 +198,6 @@ Freely available under the terms of the
<A HREF="manual.html#pdf-debug.getregistry">debug.getregistry</A><BR> <A HREF="manual.html#pdf-debug.getregistry">debug.getregistry</A><BR>
<A HREF="manual.html#pdf-debug.getupvalue">debug.getupvalue</A><BR> <A HREF="manual.html#pdf-debug.getupvalue">debug.getupvalue</A><BR>
<A HREF="manual.html#pdf-debug.getuservalue">debug.getuservalue</A><BR> <A HREF="manual.html#pdf-debug.getuservalue">debug.getuservalue</A><BR>
<A HREF="manual.html#pdf-debug.setcstacklimit">debug.setcstacklimit</A><BR>
<A HREF="manual.html#pdf-debug.sethook">debug.sethook</A><BR> <A HREF="manual.html#pdf-debug.sethook">debug.sethook</A><BR>
<A HREF="manual.html#pdf-debug.setlocal">debug.setlocal</A><BR> <A HREF="manual.html#pdf-debug.setlocal">debug.setlocal</A><BR>
<A HREF="manual.html#pdf-debug.setmetatable">debug.setmetatable</A><BR> <A HREF="manual.html#pdf-debug.setmetatable">debug.setmetatable</A><BR>
@ -475,7 +475,6 @@ Freely available under the terms of the
<A HREF="manual.html#lua_resume">lua_resume</A><BR> <A HREF="manual.html#lua_resume">lua_resume</A><BR>
<A HREF="manual.html#lua_rotate">lua_rotate</A><BR> <A HREF="manual.html#lua_rotate">lua_rotate</A><BR>
<A HREF="manual.html#lua_setallocf">lua_setallocf</A><BR> <A HREF="manual.html#lua_setallocf">lua_setallocf</A><BR>
<A HREF="manual.html#lua_setcstacklimit">lua_setcstacklimit</A><BR>
<A HREF="manual.html#lua_setfield">lua_setfield</A><BR> <A HREF="manual.html#lua_setfield">lua_setfield</A><BR>
<A HREF="manual.html#lua_setglobal">lua_setglobal</A><BR> <A HREF="manual.html#lua_setglobal">lua_setglobal</A><BR>
<A HREF="manual.html#lua_sethook">lua_sethook</A><BR> <A HREF="manual.html#lua_sethook">lua_sethook</A><BR>
@ -664,10 +663,10 @@ Freely available under the terms of the
<P CLASS="footer"> <P CLASS="footer">
Last update: Last update:
Sat May 30 08:22:18 -03 2020 Tue Nov 10 20:58:52 UTC 2020
</P> </P>
<!-- <!--
Last change: revised for Lua 5.4.0 (final) Last change: revised for Lua 5.4.2
--> -->
</BODY> </BODY>

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -2966,26 +2966,31 @@ When you interact with the Lua API,
you are responsible for ensuring consistency. you are responsible for ensuring consistency.
In particular, In particular,
<em>you are responsible for controlling stack overflow</em>. <em>you are responsible for controlling stack overflow</em>.
You can use the function <a href="#lua_checkstack"><code>lua_checkstack</code></a> When you call any API function,
to ensure that the stack has enough space for pushing new elements. you must ensure the stack has enough room to accommodate the results.
<p>
There is one exception to the above rule:
When you call a Lua function
without a fixed number of results (see <a href="#lua_call"><code>lua_call</code></a>),
Lua ensures that the stack has enough space for all results.
However, it does not ensure any extra space.
So, before pushing anything on the stack after such a call
you should use <a href="#lua_checkstack"><code>lua_checkstack</code></a>.
<p> <p>
Whenever Lua calls C, Whenever Lua calls C,
it ensures that the stack has space for it ensures that the stack has space for
at least <a name="pdf-LUA_MINSTACK"><code>LUA_MINSTACK</code></a> extra slots. at least <a name="pdf-LUA_MINSTACK"><code>LUA_MINSTACK</code></a> extra elements;
that is, you can safely push up to <code>LUA_MINSTACK</code> values into it.
<code>LUA_MINSTACK</code> is defined as 20, <code>LUA_MINSTACK</code> is defined as 20,
so that usually you do not have to worry about stack space so that usually you do not have to worry about stack space
unless your code has loops pushing elements onto the stack. unless your code has loops pushing elements onto the stack.
Whenever necessary,
you can use the function <a href="#lua_checkstack"><code>lua_checkstack</code></a>
<p> to ensure that the stack has enough space for pushing new elements.
When you call a Lua function
without a fixed number of results (see <a href="#lua_call"><code>lua_call</code></a>),
Lua ensures that the stack has enough space for all results,
but it does not ensure any extra space.
So, before pushing anything in the stack after such a call
you should use <a href="#lua_checkstack"><code>lua_checkstack</code></a>.
@ -3044,6 +3049,49 @@ which behaves like a nil value.
<h3>4.1.3 &ndash; <a name="4.1.3">Pointers to strings</a></h3>
<p>
Several functions in the API return pointers (<code>const char*</code>)
to Lua strings in the stack.
(See <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>, <a href="#lua_pushlstring"><code>lua_pushlstring</code></a>,
<a href="#lua_pushstring"><code>lua_pushstring</code></a>, and <a href="#lua_tolstring"><code>lua_tolstring</code></a>.
See also <a href="#luaL_checklstring"><code>luaL_checklstring</code></a>, <a href="#luaL_checkstring"><code>luaL_checkstring</code></a>,
and <a href="#luaL_tolstring"><code>luaL_tolstring</code></a> in the auxiliary library.)
<p>
In general,
Lua's garbage collection can free or move internal memory
and then invalidate pointers to internal strings.
To allow a safe use of these pointers,
The API guarantees that any pointer to a string in a stack index
is valid while the string value at that index is not removed from the stack.
(It can be moved to another index, though.)
When the index is a pseudo-index (referring to an upvalue),
the pointer is valid while the corresponding call is active and
the corresponding upvalue is not modified.
<p>
Some functions in the debug interface
also return pointers to strings,
namely <a href="#lua_getlocal"><code>lua_getlocal</code></a>, <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>,
<a href="#lua_setlocal"><code>lua_setlocal</code></a>, and <a href="#lua_setupvalue"><code>lua_setupvalue</code></a>.
For these functions, the pointer is guaranteed to
be valid while the caller function is active and
the given closure (if one was given) is in the stack.
<p>
Except for these guarantees,
the garbage collector is free to invalidate
any pointer to internal strings.
<h2>4.2 &ndash; <a name="4.2">C Closures</a></h2> <h2>4.2 &ndash; <a name="4.2">C Closures</a></h2>
@ -3239,7 +3287,7 @@ Therefore, if a C&nbsp;function <code>foo</code> calls an API function
and this API function yields and this API function yields
(directly or indirectly by calling another function that yields), (directly or indirectly by calling another function that yields),
Lua cannot return to <code>foo</code> any more, Lua cannot return to <code>foo</code> any more,
because the <code>longjmp</code> removes its frame from the C stack. because the <code>longjmp</code> removes its frame from the C&nbsp;stack.
<p> <p>
@ -3269,7 +3317,7 @@ After the thread resumes,
it eventually will finish running the callee function. it eventually will finish running the callee function.
However, However,
the callee function cannot return to the original function, the callee function cannot return to the original function,
because its frame in the C stack was destroyed by the yield. because its frame in the C&nbsp;stack was destroyed by the yield.
Instead, Lua calls a <em>continuation function</em>, Instead, Lua calls a <em>continuation function</em>,
which was given as an argument to the callee function. which was given as an argument to the callee function.
As the name implies, As the name implies,
@ -3389,7 +3437,7 @@ depending on the situation;
an interrogation mark '<code>?</code>' means that an interrogation mark '<code>?</code>' means that
we cannot know how many elements the function pops/pushes we cannot know how many elements the function pops/pushes
by looking only at its arguments. by looking only at its arguments.
(For instance, they may depend on what is on the stack.) (For instance, they may depend on what is in the stack.)
The third field, <code>x</code>, The third field, <code>x</code>,
tells whether the function may raise errors: tells whether the function may raise errors:
'<code>-</code>' means the function never raises any error; '<code>-</code>' means the function never raises any error;
@ -3408,7 +3456,7 @@ and therefore may raise any errors.
<p> <p>
Converts the acceptable index <code>idx</code> Converts the acceptable index <code>idx</code>
into an equivalent absolute index into an equivalent absolute index
(that is, one that does not depend on the stack top). (that is, one that does not depend on the stack size).
@ -3678,7 +3726,7 @@ of numeric arguments and returns their average and their sum:
<pre>int lua_checkstack (lua_State *L, int n);</pre> <pre>int lua_checkstack (lua_State *L, int n);</pre>
<p> <p>
Ensures that the stack has space for at least <code>n</code> extra slots, Ensures that the stack has space for at least <code>n</code> extra elements,
that is, that you can safely push up to <code>n</code> values into it. that is, that you can safely push up to <code>n</code> values into it.
It returns false if it cannot fulfill the request, It returns false if it cannot fulfill the request,
either because it would cause the stack either because it would cause the stack
@ -3686,7 +3734,7 @@ to be greater than a fixed maximum size
(typically at least several thousand elements) or (typically at least several thousand elements) or
because it cannot allocate memory for the extra space. because it cannot allocate memory for the extra space.
This function never shrinks the stack; This function never shrinks the stack;
if the stack already has space for the extra slots, if the stack already has space for the extra elements,
it is left unchanged. it is left unchanged.
@ -4443,6 +4491,10 @@ plus an associated block of raw memory with <code>size</code> bytes.
<p> <p>
The function returns the address of the block of memory. The function returns the address of the block of memory.
Lua ensures that this address is valid as long as
the corresponding userdata is alive (see <a href="#2.5">&sect;2.5</a>).
Moreover, if the userdata is marked for finalization (see <a href="#2.5.3">&sect;2.5.3</a>),
its address is valid at least until the call to its finalizer.
@ -4600,13 +4652,18 @@ except that it allows the called function to yield (see <a href="#4.5">&sect;4.5
<hr><h3><a name="lua_pop"><code>lua_pop</code></a></h3><p> <hr><h3><a name="lua_pop"><code>lua_pop</code></a></h3><p>
<span class="apii">[-n, +0, &ndash;]</span> <span class="apii">[-n, +0, <em>e</em>]</span>
<pre>void lua_pop (lua_State *L, int n);</pre> <pre>void lua_pop (lua_State *L, int n);</pre>
<p> <p>
Pops <code>n</code> elements from the stack. Pops <code>n</code> elements from the stack.
<p>
This function can run arbitrary code when removing an index
marked as to-be-closed from the stack.
@ -4688,7 +4745,7 @@ This function is equivalent to <a href="#lua_pushcclosure"><code>lua_pushcclosur
<p> <p>
Pushes onto the stack a formatted string Pushes onto the stack a formatted string
and returns a pointer to this string. and returns a pointer to this string (see <a href="#4.1.3">&sect;4.1.3</a>).
It is similar to the ISO&nbsp;C function <code>sprintf</code>, It is similar to the ISO&nbsp;C function <code>sprintf</code>,
but has two important differences. but has two important differences.
First, First,
@ -4788,7 +4845,7 @@ including embedded zeros.
<p> <p>
Returns a pointer to the internal copy of the string. Returns a pointer to the internal copy of the string (see <a href="#4.1.3">&sect;4.1.3</a>).
@ -4829,7 +4886,7 @@ the function returns.
<p> <p>
Returns a pointer to the internal copy of the string. Returns a pointer to the internal copy of the string (see <a href="#4.1.3">&sect;4.1.3</a>).
<p> <p>
@ -5273,7 +5330,7 @@ for the "newindex" event (see <a href="#2.4">&sect;2.4</a>).
<hr><h3><a name="lua_settop"><code>lua_settop</code></a></h3><p> <hr><h3><a name="lua_settop"><code>lua_settop</code></a></h3><p>
<span class="apii">[-?, +?, &ndash;]</span> <span class="apii">[-?, +?, <em>e</em>]</span>
<pre>void lua_settop (lua_State *L, int index);</pre> <pre>void lua_settop (lua_State *L, int index);</pre>
<p> <p>
@ -5284,6 +5341,11 @@ then the new elements are filled with <b>nil</b>.
If <code>index</code> is&nbsp;0, then all stack elements are removed. If <code>index</code> is&nbsp;0, then all stack elements are removed.
<p>
This function can run arbitrary code when removing an index
marked as to-be-closed from the stack.
@ -5399,7 +5461,7 @@ otherwise, returns <code>NULL</code>.
<hr><h3><a name="lua_toclose"><code>lua_toclose</code></a></h3><p> <hr><h3><a name="lua_toclose"><code>lua_toclose</code></a></h3><p>
<span class="apii">[-0, +0, <em>v</em>]</span> <span class="apii">[-0, +0, <em>m</em>]</span>
<pre>void lua_toclose (lua_State *L, int index);</pre> <pre>void lua_toclose (lua_State *L, int index);</pre>
<p> <p>
@ -5423,11 +5485,19 @@ that is equal to or below an active to-be-closed index.
<p> <p>
This function can raise an out-of-memory error. In the case of an out-of-memory error,
In that case, the value in the given index is immediately closed, the value in the given index is immediately closed,
as if it was already marked. as if it was already marked.
<p>
Note that, both in case of errors and of a regular return,
by the time the <code>__close</code> metamethod runs,
the C&nbsp;stack was already unwound,
so that any automatic C&nbsp;variable declared in the calling function
will be out of scope.
@ -5482,18 +5552,12 @@ when <code>lua_tolstring</code> is applied to keys during a table traversal.)
<p> <p>
<code>lua_tolstring</code> returns a pointer <code>lua_tolstring</code> returns a pointer
to a string inside the Lua state. to a string inside the Lua state (see <a href="#4.1.3">&sect;4.1.3</a>).
This string always has a zero ('<code>\0</code>') This string always has a zero ('<code>\0</code>')
after its last character (as in&nbsp;C), after its last character (as in&nbsp;C),
but can contain other zeros in its body. but can contain other zeros in its body.
<p>
Because Lua has garbage collection,
there is no guarantee that the pointer returned by <code>lua_tolstring</code>
will be valid after the corresponding Lua value is removed from the stack.
@ -5944,7 +6008,7 @@ true if the function is a vararg function
</li> </li>
<li><b><code>ftransfer</code>: </b> <li><b><code>ftransfer</code>: </b>
the index on the stack of the first value being "transferred", the index in the stack of the first value being "transferred",
that is, parameters in a call or return values in a return. that is, parameters in a call or return values in a return.
(The other values are in consecutive indices.) (The other values are in consecutive indices.)
Using this index, you can access and modify these values Using this index, you can access and modify these values
@ -6141,7 +6205,7 @@ an identification of the <em>activation record</em>
of the function executing at a given level. of the function executing at a given level.
Level&nbsp;0 is the current running function, Level&nbsp;0 is the current running function,
whereas level <em>n+1</em> is the function that has called level <em>n</em> whereas level <em>n+1</em> is the function that has called level <em>n</em>
(except for tail calls, which do not count on the stack). (except for tail calls, which do not count in the stack).
When called with a level greater than the stack depth, When called with a level greater than the stack depth,
<a href="#lua_getstack"><code>lua_getstack</code></a> returns 0; <a href="#lua_getstack"><code>lua_getstack</code></a> returns 0;
otherwise it returns 1. otherwise it returns 1.
@ -6218,24 +6282,6 @@ calling <a href="#lua_yield"><code>lua_yield</code></a> with <code>nresults</cod
<hr><h3><a name="lua_setcstacklimit"><code>lua_setcstacklimit</code></a></h3><p>
<span class="apii">[-0, +0, &ndash;]</span>
<pre>int (lua_setcstacklimit) (lua_State *L, unsigned int limit);</pre>
<p>
Sets a new limit for the C stack.
This limit controls how deeply nested calls can go in Lua,
with the intent of avoiding a stack overflow.
Returns the old limit in case of success,
or zero in case of error.
For more details about this function,
see <a href="#pdf-debug.setcstacklimit"><code>debug.setcstacklimit</code></a>,
its equivalent in the standard library.
<hr><h3><a name="lua_sethook"><code>lua_sethook</code></a></h3><p> <hr><h3><a name="lua_sethook"><code>lua_sethook</code></a></h3><p>
<span class="apii">[-0, +0, &ndash;]</span> <span class="apii">[-0, +0, &ndash;]</span>
<pre>void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);</pre> <pre>void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);</pre>
@ -6259,8 +6305,7 @@ For each event, the hook is called as explained below:
<ul> <ul>
<li><b>The call hook: </b> is called when the interpreter calls a function. <li><b>The call hook: </b> is called when the interpreter calls a function.
The hook is called just after Lua enters the new function, The hook is called just after Lua enters the new function.
before the function gets its arguments.
</li> </li>
<li><b>The return hook: </b> is called when the interpreter returns from a function. <li><b>The return hook: </b> is called when the interpreter returns from a function.
@ -7573,7 +7618,7 @@ it returns <code>NULL</code> instead of raising an error.
Converts any Lua value at the given index to a C&nbsp;string Converts any Lua value at the given index to a C&nbsp;string
in a reasonable format. in a reasonable format.
The resulting string is pushed onto the stack and also The resulting string is pushed onto the stack and also
returned by the function. returned by the function (see <a href="#4.1.3">&sect;4.1.3</a>).
If <code>len</code> is not <code>NULL</code>, If <code>len</code> is not <code>NULL</code>,
the function also sets <code>*len</code> with the string length. the function also sets <code>*len</code> with the string length.
@ -8001,9 +8046,11 @@ The default is "<code>bt</code>".
<p> <p>
Lua does not check the consistency of binary chunks. It is safe to load malformed binary chunks;
Maliciously crafted binary chunks can crash <code>load</code> signals an appropriate error.
the interpreter. However,
Lua does not check the consistency of the code inside binary chunks;
running maliciously crafted bytecode can crash the interpreter.
@ -8665,6 +8712,18 @@ As such, it is only available on some platforms
plus other Unix systems that support the <code>dlfcn</code> standard). plus other Unix systems that support the <code>dlfcn</code> standard).
<p>
This function is inherently insecure,
as it allows Lua to call any function in any readable dynamic
library in the system.
(Lua calls any function assuming the function
has a proper prototype and respects a proper protocol
(see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
Therefore,
calling an arbitrary function in an arbitrary dynamic library
more often than not results in an access violation.)
<p> <p>
@ -11084,7 +11143,7 @@ which means the function running at level <code>f</code> of the call stack
of the given thread: of the given thread:
level&nbsp;0 is the current function (<code>getinfo</code> itself); level&nbsp;0 is the current function (<code>getinfo</code> itself);
level&nbsp;1 is the function that called <code>getinfo</code> level&nbsp;1 is the function that called <code>getinfo</code>
(except for tail calls, which do not count on the stack); (except for tail calls, which do not count in the stack);
and so on. and so on.
If <code>f</code> is a number greater than the number of active functions, If <code>f</code> is a number greater than the number of active functions,
then <code>getinfo</code> returns <b>fail</b>. then <code>getinfo</code> returns <b>fail</b>.
@ -11218,43 +11277,6 @@ to the userdata <code>u</code> plus a boolean,
<p>
<hr><h3><a name="pdf-debug.setcstacklimit"><code>debug.setcstacklimit (limit)</code></a></h3>
<p>
Sets a new limit for the C stack.
This limit controls how deeply nested calls can go in Lua,
with the intent of avoiding a stack overflow.
A limit too small restricts recursive calls pointlessly;
a limit too large exposes the interpreter to stack-overflow crashes.
Unfortunately, there is no way to know a priori
the maximum safe limit for a platform.
<p>
Each call made from Lua code counts one unit.
Other operations (e.g., calls made from C to Lua or resuming a coroutine)
may have a higher cost.
<p>
This function has the following restrictions:
<ul>
<li>It can only be called from the main coroutine (thread);</li>
<li>It cannot be called while handling a stack-overflow error;</li>
<li><code>limit</code> must be less than 40000;</li>
<li><code>limit</code> cannot be less than the amount of C stack in use.</li>
</ul><p>
If a call does not respect some restriction,
it returns a false value.
Otherwise,
the call returns the old limit.
<p> <p>
<hr><h3><a name="pdf-debug.sethook"><code>debug.sethook ([thread,] hook, mask [, count])</code></a></h3> <hr><h3><a name="pdf-debug.sethook"><code>debug.sethook ([thread,] hook, mask [, count])</code></a></h3>
@ -11886,10 +11908,10 @@ and LiteralString, see <a href="#3.1">&sect;3.1</a>.)
<P CLASS="footer"> <P CLASS="footer">
Last update: Last update:
Thu Jun 18 16:10:16 UTC 2020 Fri Nov 13 15:35:22 UTC 2020
</P> </P>
<!-- <!--
Last change: revised for Lua 5.4.0 (final) Last change: revised for Lua 5.4.2
--> -->
</body></html> </body></html>

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -110,7 +110,7 @@ Here are the details.
<OL> <OL>
<LI> <LI>
Open a terminal window and move to Open a terminal window and move to
the top-level directory, which is named <TT>lua-5.4.0</TT>. the top-level directory, which is named <TT>lua-5.4.2</TT>.
The <TT>Makefile</TT> there controls both the build process and the installation process. The <TT>Makefile</TT> there controls both the build process and the installation process.
<P> <P>
<LI> <LI>
@ -330,10 +330,10 @@ THE SOFTWARE.
<P CLASS="footer"> <P CLASS="footer">
Last update: Last update:
Fri May 1 19:33:31 UTC 2020 Tue Nov 10 20:55:28 UTC 2020
</P> </P>
<!-- <!--
Last change: revised for Lua 5.4.0 (final) Last change: revised for Lua 5.4.2
--> -->
</BODY> </BODY>

View File

@ -18,7 +18,7 @@
#define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4" #define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "0" #define LUA_VERSION_RELEASE "2"
#define LUA_VERSION_NUM 504 #define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)

View File

@ -36,21 +36,6 @@
** ===================================================================== ** =====================================================================
*/ */
/*
@@ LUAI_MAXCSTACK defines the maximum depth for nested calls and
** also limits the maximum depth of other recursive algorithms in
** the implementation, such as syntactic analysis. A value too
** large may allow the interpreter to crash (C-stack overflow).
** The default value seems ok for regular machines, but may be
** too high for restricted hardware.
** The test file 'cstack.lua' may help finding a good limit.
** (It will crash with a limit too high.)
*/
#if !defined(LUAI_MAXCSTACK)
#define LUAI_MAXCSTACK 2000
#endif
/* /*
@@ LUA_USE_C89 controls the use of non-ISO-C89 features. @@ LUA_USE_C89 controls the use of non-ISO-C89 features.
** Define it if you want Lua to avoid the use of a few C99 features ** Define it if you want Lua to avoid the use of a few C99 features

View File

@ -26,7 +26,7 @@ MYLIBS=
MYOBJS= MYOBJS=
# Special flags for compiler modules; -Os reduces code size. # Special flags for compiler modules; -Os reduces code size.
CMCFLAGS= -Os CMCFLAGS=
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======

View File

@ -97,8 +97,9 @@ static StkId index2stack (lua_State *L, int idx) {
LUA_API int lua_checkstack (lua_State *L, int n) { LUA_API int lua_checkstack (lua_State *L, int n) {
int res; int res;
CallInfo *ci = L->ci; CallInfo *ci;
lua_lock(L); lua_lock(L);
ci = L->ci;
api_check(L, n >= 0, "negative 'n'"); api_check(L, n >= 0, "negative 'n'");
if (L->stack_last - L->top > n) /* stack large enough? */ if (L->stack_last - L->top > n) /* stack large enough? */
res = 1; /* yes; check is OK */ res = 1; /* yes; check is OK */
@ -170,10 +171,12 @@ LUA_API int lua_gettop (lua_State *L) {
LUA_API void lua_settop (lua_State *L, int idx) { LUA_API void lua_settop (lua_State *L, int idx) {
CallInfo *ci = L->ci; CallInfo *ci;
StkId func = ci->func; StkId func;
ptrdiff_t diff; /* difference for new top */ ptrdiff_t diff; /* difference for new top */
lua_lock(L); lua_lock(L);
ci = L->ci;
func = ci->func;
if (idx >= 0) { if (idx >= 0) {
api_check(L, idx <= ci->top - (func + 1), "new top too large"); api_check(L, idx <= ci->top - (func + 1), "new top too large");
diff = ((func + 1) + idx) - L->top; diff = ((func + 1) + idx) - L->top;
@ -376,20 +379,22 @@ LUA_API int lua_toboolean (lua_State *L, int idx) {
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
TValue *o = index2value(L, idx); TValue *o;
lua_lock(L);
o = index2value(L, idx);
if (!ttisstring(o)) { if (!ttisstring(o)) {
if (!cvt2str(o)) { /* not convertible? */ if (!cvt2str(o)) { /* not convertible? */
if (len != NULL) *len = 0; if (len != NULL) *len = 0;
lua_unlock(L);
return NULL; return NULL;
} }
lua_lock(L); /* 'luaO_tostring' may create a new string */
luaO_tostring(L, o); luaO_tostring(L, o);
luaC_checkGC(L); luaC_checkGC(L);
o = index2value(L, idx); /* previous call may reallocate the stack */ o = index2value(L, idx); /* previous call may reallocate the stack */
lua_unlock(L);
} }
if (len != NULL) if (len != NULL)
*len = vslen(o); *len = vslen(o);
lua_unlock(L);
return svalue(o); return svalue(o);
} }
@ -563,6 +568,7 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
while (n--) { while (n--) {
setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); setobj2n(L, &cl->upvalue[n], s2v(L->top + n));
/* does not need barrier because closure is white */ /* does not need barrier because closure is white */
lua_assert(iswhite(cl));
} }
setclCvalue(L, s2v(L->top), cl); setclCvalue(L, s2v(L->top), cl);
api_incr_top(L); api_incr_top(L);
@ -624,8 +630,9 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
LUA_API int lua_getglobal (lua_State *L, const char *name) { LUA_API int lua_getglobal (lua_State *L, const char *name) {
Table *reg = hvalue(&G(L)->l_registry); Table *reg;
lua_lock(L); lua_lock(L);
reg = hvalue(&G(L)->l_registry);
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
} }
@ -804,8 +811,9 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
LUA_API void lua_setglobal (lua_State *L, const char *name) { LUA_API void lua_setglobal (lua_State *L, const char *name) {
Table *reg = hvalue(&G(L)->l_registry); Table *reg;
lua_lock(L); /* unlock done in 'auxsetstr' */ lua_lock(L); /* unlock done in 'auxsetstr' */
reg = hvalue(&G(L)->l_registry);
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
} }
@ -1093,8 +1101,9 @@ LUA_API int lua_status (lua_State *L) {
LUA_API int lua_gc (lua_State *L, int what, ...) { LUA_API int lua_gc (lua_State *L, int what, ...) {
va_list argp; va_list argp;
int res = 0; int res = 0;
global_State *g = G(L); global_State *g;
lua_lock(L); lua_lock(L);
g = G(L);
va_start(argp, what); va_start(argp, what);
switch (what) { switch (what) {
case LUA_GCSTOP: { case LUA_GCSTOP: {
@ -1194,9 +1203,15 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
LUA_API int lua_error (lua_State *L) { LUA_API int lua_error (lua_State *L) {
TValue *errobj;
lua_lock(L); lua_lock(L);
errobj = s2v(L->top - 1);
api_checknelems(L, 1); api_checknelems(L, 1);
luaG_errormsg(L); /* error object is the memory error message? */
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
luaM_error(L); /* raise a memory error */
else
luaG_errormsg(L); /* raise a regular error */
/* code unreachable; will unlock when control actually leaves the kernel */ /* code unreachable; will unlock when control actually leaves the kernel */
return 0; /* to avoid warnings */ return 0; /* to avoid warnings */
} }
@ -1238,14 +1253,12 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
LUA_API void lua_concat (lua_State *L, int n) { LUA_API void lua_concat (lua_State *L, int n) {
lua_lock(L); lua_lock(L);
api_checknelems(L, n); api_checknelems(L, n);
if (n >= 2) { if (n > 0)
luaV_concat(L, n); luaV_concat(L, n);
} else { /* nothing to concatenate */
else if (n == 0) { /* push empty string */ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
api_incr_top(L); api_incr_top(L);
} }
/* else n == 1; nothing to do */
luaC_checkGC(L); luaC_checkGC(L);
lua_unlock(L); lua_unlock(L);
} }
@ -1370,13 +1383,16 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
static const UpVal *const nullup = NULL;
LClosure *f; LClosure *f;
TValue *fi = index2value(L, fidx); TValue *fi = index2value(L, fidx);
api_check(L, ttisLclosure(fi), "Lua function expected"); api_check(L, ttisLclosure(fi), "Lua function expected");
f = clLvalue(fi); f = clLvalue(fi);
api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
if (pf) *pf = f; if (pf) *pf = f;
if (1 <= n && n <= f->p->sizeupvalues)
return &f->upvals[n - 1]; /* get its upvalue pointer */ return &f->upvals[n - 1]; /* get its upvalue pointer */
else
return (UpVal**)&nullup;
} }
@ -1388,11 +1404,14 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
} }
case LUA_VCCL: { /* C closure */ case LUA_VCCL: { /* C closure */
CClosure *f = clCvalue(fi); CClosure *f = clCvalue(fi);
api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); if (1 <= n && n <= f->nupvalues)
return &f->upvalue[n - 1]; return &f->upvalue[n - 1];
} /* else */
} /* FALLTHROUGH */
case LUA_VLCF:
return NULL; /* light C functions have no upvalues */
default: { default: {
api_check(L, 0, "closure expected"); api_check(L, 0, "function expected");
return NULL; return NULL;
} }
} }
@ -1404,6 +1423,7 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
LClosure *f1; LClosure *f1;
UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
UpVal **up2 = getupvalref(L, fidx2, n2, NULL); UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
*up1 = *up2; *up1 = *up2;
luaC_objbarrier(L, f1, *up1); luaC_objbarrier(L, f1, *up1);
} }

View File

@ -283,10 +283,10 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
LUALIB_API int luaL_execresult (lua_State *L, int stat) { LUALIB_API int luaL_execresult (lua_State *L, int stat) {
const char *what = "exit"; /* type of termination */
if (stat != 0 && errno != 0) /* error with an 'errno'? */ if (stat != 0 && errno != 0) /* error with an 'errno'? */
return luaL_fileresult(L, 0, NULL); return luaL_fileresult(L, 0, NULL);
else { else {
const char *what = "exit"; /* type of termination */
l_inspectstat(stat, what); /* interpret result */ l_inspectstat(stat, what); /* interpret result */
if (*what == 'e' && stat == 0) /* successful termination? */ if (*what == 'e' && stat == 0) /* successful termination? */
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
@ -475,8 +475,10 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
lua_Alloc allocf = lua_getallocf(L, &ud); lua_Alloc allocf = lua_getallocf(L, &ud);
UBox *box = (UBox *)lua_touserdata(L, idx); UBox *box = (UBox *)lua_touserdata(L, idx);
void *temp = allocf(ud, box->box, box->bsize, newsize); void *temp = allocf(ud, box->box, box->bsize, newsize);
if (temp == NULL && newsize > 0) /* allocation error? */ if (temp == NULL && newsize > 0) { /* allocation error? */
luaL_error(L, "not enough memory"); lua_pushliteral(L, "not enough memory");
lua_error(L); /* raise a memory error */
}
box->box = temp; box->box = temp;
box->bsize = newsize; box->bsize = newsize;
return temp; return temp;
@ -1004,43 +1006,67 @@ static int panic (lua_State *L) {
/* /*
** Emit a warning. '*warnstate' means: ** Warning functions:
** 0 - warning system is off; ** warnfoff: warning system is off
** 1 - ready to start a new message; ** warnfon: ready to start a new message
** 2 - previous message is to be continued. ** warnfcont: previous message is to be continued
*/ */
static void warnf (void *ud, const char *message, int tocont) { static void warnfoff (void *ud, const char *message, int tocont);
int *warnstate = (int *)ud; static void warnfon (void *ud, const char *message, int tocont);
if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */ static void warnfcont (void *ud, const char *message, int tocont);
if (strcmp(message, "@off") == 0)
*warnstate = 0;
else if (strcmp(message, "@on") == 0) /*
*warnstate = 1; ** Check whether message is a control message. If so, execute the
return; ** control or ignore it if unknown.
*/
static int checkcontrol (lua_State *L, const char *message, int tocont) {
if (tocont || *(message++) != '@') /* not a control message? */
return 0;
else {
if (strcmp(message, "off") == 0)
lua_setwarnf(L, warnfoff, L); /* turn warnings off */
else if (strcmp(message, "on") == 0)
lua_setwarnf(L, warnfon, L); /* turn warnings on */
return 1; /* it was a control message */
} }
else if (*warnstate == 0) /* warnings off? */ }
return;
if (*warnstate == 1) /* previous message was the last? */
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ static void warnfoff (void *ud, const char *message, int tocont) {
checkcontrol((lua_State *)ud, message, tocont);
}
/*
** Writes the message and handle 'tocont', finishing the message
** if needed and setting the next warn function.
*/
static void warnfcont (void *ud, const char *message, int tocont) {
lua_State *L = (lua_State *)ud;
lua_writestringerror("%s", message); /* write message */ lua_writestringerror("%s", message); /* write message */
if (tocont) /* not the last part? */ if (tocont) /* not the last part? */
*warnstate = 2; /* to be continued */ lua_setwarnf(L, warnfcont, L); /* to be continued */
else { /* last part */ else { /* last part */
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
*warnstate = 1; /* ready to start a new message */ lua_setwarnf(L, warnfon, L); /* next call is a new message */
} }
} }
static void warnfon (void *ud, const char *message, int tocont) {
if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */
return; /* nothing else to be done */
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
warnfcont(ud, message, tocont); /* finish processing */
}
LUALIB_API lua_State *luaL_newstate (void) { LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL); lua_State *L = lua_newstate(l_alloc, NULL);
if (L) { if (L) {
int *warnstate; /* space for warning state */
lua_atpanic(L, &panic); lua_atpanic(L, &panic);
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0); lua_setwarnf(L, warnfoff, L); /* default is warnings off */
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
*warnstate = 0; /* default is warnings off */
lua_setwarnf(L, warnf, warnstate);
} }
return L; return L;
} }

View File

@ -753,7 +753,7 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
/* /*
** Ensure that expression 'e' is not a variable (nor a constant). ** Ensure that expression 'e' is not a variable (nor a <const>).
** (Expression still may have jump lists.) ** (Expression still may have jump lists.)
*/ */
void luaK_dischargevars (FuncState *fs, expdesc *e) { void luaK_dischargevars (FuncState *fs, expdesc *e) {
@ -805,8 +805,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
/* /*
** Ensures expression value is in register 'reg' (and therefore ** Ensure expression value is in register 'reg', making 'e' a
** 'e' will become a non-relocatable expression). ** non-relocatable expression.
** (Expression still may have jump lists.) ** (Expression still may have jump lists.)
*/ */
static void discharge2reg (FuncState *fs, expdesc *e, int reg) { static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
@ -860,7 +860,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
/* /*
** Ensures expression value is in any register. ** Ensure expression value is in a register, making 'e' a
** non-relocatable expression.
** (Expression still may have jump lists.) ** (Expression still may have jump lists.)
*/ */
static void discharge2anyreg (FuncState *fs, expdesc *e) { static void discharge2anyreg (FuncState *fs, expdesc *e) {
@ -946,8 +947,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
exp2reg(fs, e, e->u.info); /* put final result in it */ exp2reg(fs, e, e->u.info); /* put final result in it */
return e->u.info; return e->u.info;
} }
/* else expression has jumps and cannot change its register
to hold the jump values, because it is a local variable.
Go through to the default case. */
} }
luaK_exp2nextreg(fs, e); /* otherwise, use next available register */ luaK_exp2nextreg(fs, e); /* default: use next available register */
return e->u.info; return e->u.info;
} }

View File

@ -73,11 +73,12 @@ static int luaB_coresume (lua_State *L) {
static int luaB_auxwrap (lua_State *L) { static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1)); lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L)); int r = auxresume(L, co, lua_gettop(L));
if (r < 0) { if (r < 0) { /* error? */
int stat = lua_status(co); int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD) if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
lua_resetthread(co); /* close variables in case of errors */ lua_resetthread(co); /* close its tbc variables */
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ if (stat != LUA_ERRMEM && /* not a memory error and ... */
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
luaL_where(L, 1); /* add extra info, if available */ luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2); lua_insert(L, -2);
lua_concat(L, 2); lua_concat(L, 2);

View File

@ -13,7 +13,7 @@
/* /*
** WARNING: the functions defined here do not necessarily correspond ** WARNING: the functions defined here do not necessarily correspond
** to the similar functions in the standard C ctype.h. They are ** to the similar functions in the standard C ctype.h. They are
** optimized for the specific needs of Lua ** optimized for the specific needs of Lua.
*/ */
#if !defined(LUA_USE_CTYPE) #if !defined(LUA_USE_CTYPE)
@ -61,13 +61,19 @@
#define lisprint(c) testprop(c, MASK(PRINTBIT)) #define lisprint(c) testprop(c, MASK(PRINTBIT))
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) #define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
/* /*
** this 'ltolower' only works for alphabetic characters ** In ASCII, this 'ltolower' is correct for alphabetic characters and
** for '.'. That is enough for Lua needs. ('check_exp' ensures that
** the character either is an upper-case letter or is unchanged by
** the transformation, which holds for lower-case letters and '.'.)
*/ */
#define ltolower(c) ((c) | ('A' ^ 'a')) #define ltolower(c) \
check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
(c) | ('A' ^ 'a'))
/* two more entries for 0 and -1 (EOZ) */ /* one entry for each character and for -1 (EOZ) */
LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)

View File

@ -281,25 +281,33 @@ static int db_setupvalue (lua_State *L) {
** Check whether a given upvalue from a given closure exists and ** Check whether a given upvalue from a given closure exists and
** returns its index ** returns its index
*/ */
static int checkupval (lua_State *L, int argf, int argnup) { static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
void *id;
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup, id = lua_upvalueid(L, argf, nup);
"invalid upvalue index"); if (pnup) {
return nup; luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
*pnup = nup;
}
return id;
} }
static int db_upvalueid (lua_State *L) { static int db_upvalueid (lua_State *L) {
int n = checkupval(L, 1, 2); void *id = checkupval(L, 1, 2, NULL);
lua_pushlightuserdata(L, lua_upvalueid(L, 1, n)); if (id != NULL)
lua_pushlightuserdata(L, id);
else
luaL_pushfail(L);
return 1; return 1;
} }
static int db_upvaluejoin (lua_State *L) { static int db_upvaluejoin (lua_State *L) {
int n1 = checkupval(L, 1, 2); int n1, n2;
int n2 = checkupval(L, 3, 4); checkupval(L, 1, 2, &n1);
checkupval(L, 3, 4, &n2);
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
lua_upvaluejoin(L, 1, n1, 3, n2); lua_upvaluejoin(L, 1, n1, 3, n2);
@ -440,9 +448,6 @@ static int db_traceback (lua_State *L) {
static int db_setcstacklimit (lua_State *L) { static int db_setcstacklimit (lua_State *L) {
int limit = (int)luaL_checkinteger(L, 1); int limit = (int)luaL_checkinteger(L, 1);
int res = lua_setcstacklimit(L, limit); int res = lua_setcstacklimit(L, limit);
if (res == 0)
lua_pushboolean(L, 0);
else
lua_pushinteger(L, res); lua_pushinteger(L, res);
return 1; return 1;
} }

View File

@ -33,10 +33,8 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
/* inverse of 'pcRel' */
/* Active Lua function (given call info) */ #define invpcRel(pc, p) ((p)->code + (pc) + 1)
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
static const char *funcnamefromcode (lua_State *L, CallInfo *ci, static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
const char **name); const char **name);
@ -127,20 +125,18 @@ static void settraps (CallInfo *ci) {
/* /*
** This function can be called during a signal, under "reasonable" ** This function can be called during a signal, under "reasonable"
** assumptions. ** assumptions.
** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by ** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
** 'resethookcount') are for debug only, and it is no problem if they ** are for debug only, and it is no problem if they get arbitrary
** get arbitrary values (causes at most one wrong hook call). 'hookmask' ** values (causes at most one wrong hook call). 'hookmask' is an atomic
** is an atomic value. We assume that pointers are atomic too (e.g., gcc ** value. We assume that pointers are atomic too (e.g., gcc ensures that
** ensures that for all platforms where it runs). Moreover, 'hook' is ** for all platforms where it runs). Moreover, 'hook' is always checked
** always checked before being called (see 'luaD_hook'). ** before being called (see 'luaD_hook').
*/ */
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
if (func == NULL || mask == 0) { /* turn off hooks? */ if (func == NULL || mask == 0) { /* turn off hooks? */
mask = 0; mask = 0;
func = NULL; func = NULL;
} }
if (isLua(L->ci))
L->oldpc = L->ci->u.l.savedpc;
L->hook = func; L->hook = func;
L->basehookcount = count; L->basehookcount = count;
resethookcount(L); resethookcount(L);
@ -192,8 +188,8 @@ static const char *upvalname (const Proto *p, int uv) {
static const char *findvararg (CallInfo *ci, int n, StkId *pos) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func))->p->is_vararg) { if (clLvalue(s2v(ci->func))->p->is_vararg) {
int nextra = ci->u.l.nextraargs; int nextra = ci->u.l.nextraargs;
if (n <= nextra) { if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func - nextra + (n - 1); *pos = ci->func - nextra - (n + 1);
return "(vararg)"; /* generic name for any vararg */ return "(vararg)"; /* generic name for any vararg */
} }
} }
@ -206,7 +202,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
const char *name = NULL; const char *name = NULL;
if (isLua(ci)) { if (isLua(ci)) {
if (n < 0) /* access to vararg values? */ if (n < 0) /* access to vararg values? */
return findvararg(ci, -n, pos); return findvararg(ci, n, pos);
else else
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
} }
@ -787,18 +783,34 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
** previous instruction 'oldpc'. ** previous instruction 'oldpc'.
*/ */
static int changedline (const Proto *p, int oldpc, int newpc) { static int changedline (const Proto *p, int oldpc, int newpc) {
if (p->lineinfo == NULL) /* no debug information? */
return 0;
while (oldpc++ < newpc) { while (oldpc++ < newpc) {
if (p->lineinfo[oldpc] != 0) if (p->lineinfo[oldpc] != 0)
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc)); return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
} }
return 0; /* no line changes in the way */ return 0; /* no line changes between positions */
} }
/*
** Traces the execution of a Lua function. Called before the execution
** of each opcode, when debug is on. 'L->oldpc' stores the last
** instruction traced, to detect line changes. When entering a new
** function, 'npci' will be zero and will test as a new line without
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
** before. Some exceptional conditions may return to a function without
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
** call to a line hook.)
*/
int luaG_traceexec (lua_State *L, const Instruction *pc) { int luaG_traceexec (lua_State *L, const Instruction *pc) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
lu_byte mask = L->hookmask; lu_byte mask = L->hookmask;
const Proto *p = ci_func(ci)->p;
int counthook; int counthook;
/* 'L->oldpc' may be invalid; reset it in this case */
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
ci->u.l.trap = 0; /* don't need to stop again */ ci->u.l.trap = 0; /* don't need to stop again */
return 0; /* turn off 'trap' */ return 0; /* turn off 'trap' */
@ -819,15 +831,14 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
if (counthook) if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
if (mask & LUA_MASKLINE) { if (mask & LUA_MASKLINE) {
const Proto *p = ci_func(ci)->p;
int npci = pcRel(pc, p); int npci = pcRel(pc, p);
if (npci == 0 || /* call linehook when enter a new function, */ if (npci == 0 || /* call linehook when enter a new function, */
pc <= L->oldpc || /* when jump back (loop), or when */ pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
changedline(p, pcRel(L->oldpc, p), npci)) { /* enter new line */ changedline(p, oldpc, npci)) { /* enter new line */
int newline = luaG_getfuncline(p, npci); int newline = luaG_getfuncline(p, npci);
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
} }
L->oldpc = pc; /* 'pc' of last call to line hook */ L->oldpc = npci; /* 'pc' of last call to line hook */
} }
if (L->status == LUA_YIELD) { /* did hook yield? */ if (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook) if (counthook)

View File

@ -13,6 +13,11 @@
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) #define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
/* Active Lua function (given call info) */
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
#define resethookcount(L) (L->hookcount = L->basehookcount) #define resethookcount(L) (L->hookcount = L->basehookcount)
/* /*

View File

@ -139,8 +139,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
global_State *g = G(L); l_uint32 oldnCcalls = L->nCcalls;
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
struct lua_longjmp lj; struct lua_longjmp lj;
lj.status = LUA_OK; lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */ lj.previous = L->errorJmp; /* chain new error handler */
@ -149,7 +148,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
(*f)(L, ud); (*f)(L, ud);
); );
L->errorJmp = lj.previous; /* restore old error handler */ L->errorJmp = lj.previous; /* restore old error handler */
L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci; L->nCcalls = oldnCcalls;
return lj.status; return lj.status;
} }
@ -183,21 +182,20 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
int lim = L->stacksize; int lim = stacksize(L);
StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue); StkId newstack = luaM_reallocvector(L, L->stack,
lim + EXTRA_STACK, newsize + EXTRA_STACK, StackValue);
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
if (unlikely(newstack == NULL)) { /* reallocation failed? */ if (unlikely(newstack == NULL)) { /* reallocation failed? */
if (raiseerror) if (raiseerror)
luaM_error(L); luaM_error(L);
else return 0; /* do not raise an error */ else return 0; /* do not raise an error */
} }
for (; lim < newsize; lim++) for (; lim < newsize; lim++)
setnilvalue(s2v(newstack + lim)); /* erase new segment */ setnilvalue(s2v(newstack + lim + EXTRA_STACK)); /* erase new segment */
correctstack(L, L->stack, newstack); correctstack(L, L->stack, newstack);
L->stack = newstack; L->stack = newstack;
L->stacksize = newsize; L->stack_last = L->stack + newsize;
L->stack_last = L->stack + newsize - EXTRA_STACK;
return 1; return 1;
} }
@ -207,52 +205,73 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
** is true, raises any error; otherwise, return 0 in case of errors. ** is true, raises any error; otherwise, return 0 in case of errors.
*/ */
int luaD_growstack (lua_State *L, int n, int raiseerror) { int luaD_growstack (lua_State *L, int n, int raiseerror) {
int size = L->stacksize; int size = stacksize(L);
int newsize = 2 * size; /* tentative new size */ if (unlikely(size > LUAI_MAXSTACK)) {
if (unlikely(size > LUAI_MAXSTACK)) { /* need more space after extra size? */ /* if stack is larger than maximum, thread is already using the
extra space reserved for errors, that is, thread is handling
a stack error; cannot grow further than that. */
lua_assert(stacksize(L) == ERRORSTACKSIZE);
if (raiseerror) if (raiseerror)
luaD_throw(L, LUA_ERRERR); /* error inside message handler */ luaD_throw(L, LUA_ERRERR); /* error inside message handler */
else return 0; return 0; /* if not 'raiseerror', just signal it */
} }
else { else {
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; int newsize = 2 * size; /* tentative new size */
int needed = cast_int(L->top - L->stack) + n;
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
newsize = LUAI_MAXSTACK; newsize = LUAI_MAXSTACK;
if (newsize < needed) /* but must respect what was asked for */ if (newsize < needed) /* but must respect what was asked for */
newsize = needed; newsize = needed;
if (unlikely(newsize > LUAI_MAXSTACK)) { /* stack overflow? */ if (likely(newsize <= LUAI_MAXSTACK))
return luaD_reallocstack(L, newsize, raiseerror);
else { /* stack overflow */
/* add extra size to be able to handle the error message */ /* add extra size to be able to handle the error message */
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
if (raiseerror) if (raiseerror)
luaG_runerror(L, "stack overflow"); luaG_runerror(L, "stack overflow");
else return 0; return 0;
}
} }
} /* else no errors */
return luaD_reallocstack(L, newsize, raiseerror);
} }
static int stackinuse (lua_State *L) { static int stackinuse (lua_State *L) {
CallInfo *ci; CallInfo *ci;
int res;
StkId lim = L->top; StkId lim = L->top;
for (ci = L->ci; ci != NULL; ci = ci->previous) { for (ci = L->ci; ci != NULL; ci = ci->previous) {
if (lim < ci->top) lim = ci->top; if (lim < ci->top) lim = ci->top;
} }
lua_assert(lim <= L->stack_last); lua_assert(lim <= L->stack_last);
return cast_int(lim - L->stack) + 1; /* part of stack in use */ res = cast_int(lim - L->stack) + 1; /* part of stack in use */
if (res < LUA_MINSTACK)
res = LUA_MINSTACK; /* ensure a minimum size */
return res;
} }
/*
** If stack size is more than 3 times the current use, reduce that size
** to twice the current use. (So, the final stack size is at most 2/3 the
** previous size, and half of its entries are empty.)
** As a particular case, if stack was handling a stack overflow and now
** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than
** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
** will be reduced to a "regular" size.
*/
void luaD_shrinkstack (lua_State *L) { void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L); int inuse = stackinuse(L);
int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK; int nsize = inuse * 2; /* proposed new size */
if (goodsize > LUAI_MAXSTACK) int max = inuse * 3; /* maximum "reasonable" size */
goodsize = LUAI_MAXSTACK; /* respect stack limit */ if (max > LUAI_MAXSTACK) {
max = LUAI_MAXSTACK; /* respect stack limit */
if (nsize > LUAI_MAXSTACK)
nsize = LUAI_MAXSTACK;
}
/* if thread is currently not handling a stack overflow and its /* if thread is currently not handling a stack overflow and its
good size is smaller than current size, shrink its stack */ size is larger than maximum "reasonable" size, shrink it */
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && if (inuse <= LUAI_MAXSTACK && stacksize(L) > max)
goodsize < L->stacksize) luaD_reallocstack(L, nsize, 0); /* ok if that fails */
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */
else /* don't change stack */ else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */ condmovestack(L,{},{}); /* (change only for debugging) */
luaE_shrinkCI(L); /* shrink CI list */ luaE_shrinkCI(L); /* shrink CI list */
@ -328,7 +347,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
int delta = 0; int delta = 0;
if (isLuacode(ci)) { if (isLuacode(ci)) {
Proto *p = clLvalue(s2v(ci->func))->p; Proto *p = ci_func(ci)->p;
if (p->is_vararg) if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1; delta = ci->u.l.nextraargs + p->numparams + 1;
if (L->top < ci->top) if (L->top < ci->top)
@ -341,15 +360,15 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
ci->func -= delta; ci->func -= delta;
} }
if (isLua(ci->previous)) if (isLua(ci = ci->previous))
L->oldpc = ci->previous->u.l.savedpc; /* update 'oldpc' */ L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
return restorestack(L, oldtop); return restorestack(L, oldtop);
} }
/* /*
** Check whether 'func' has a '__call' metafield. If so, put it in the ** Check whether 'func' has a '__call' metafield. If so, put it in the
** stack, below original 'func', so that 'luaD_call' can call it. Raise ** stack, below original 'func', so that 'luaD_precall' can call it. Raise
** an error if there is no '__call' metafield. ** an error if there is no '__call' metafield.
*/ */
void luaD_tryfuncTM (lua_State *L, StkId func) { void luaD_tryfuncTM (lua_State *L, StkId func) {
@ -450,12 +469,14 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
/* /*
** Call a function (C or Lua). The function to be called is at *func. ** Prepares the call to a function (C or Lua). For C functions, also do
** The arguments are on the stack, right after the function. ** the call. The function to be called is at '*func'. The arguments
** When returns, all the results are on the stack, starting at the original ** are on the stack, right after the function. Returns the CallInfo
** function position. ** to be executed, if it was a Lua function. Otherwise (a C function)
** returns NULL, with all the results on the stack, starting at the
** original function position.
*/ */
void luaD_call (lua_State *L, StkId func, int nresults) { CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
lua_CFunction f; lua_CFunction f;
retry: retry:
switch (ttypetag(s2v(func))) { switch (ttypetag(s2v(func))) {
@ -466,13 +487,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
f = fvalue(s2v(func)); f = fvalue(s2v(func));
Cfunc: { Cfunc: {
int n; /* number of returns */ int n; /* number of returns */
CallInfo *ci = next_ci(L); CallInfo *ci;
checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
L->ci = ci = next_ci(L);
ci->nresults = nresults; ci->nresults = nresults;
ci->callstatus = CIST_C; ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK; ci->top = L->top + LUA_MINSTACK;
ci->func = func; ci->func = func;
L->ci = ci;
lua_assert(ci->top <= L->stack_last); lua_assert(ci->top <= L->stack_last);
if (L->hookmask & LUA_MASKCALL) { if (L->hookmask & LUA_MASKCALL) {
int narg = cast_int(L->top - func) - 1; int narg = cast_int(L->top - func) - 1;
@ -483,29 +504,28 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
lua_lock(L); lua_lock(L);
api_checknelems(L, n); api_checknelems(L, n);
luaD_poscall(L, ci, n); luaD_poscall(L, ci, n);
break; return NULL;
} }
case LUA_VLCL: { /* Lua function */ case LUA_VLCL: { /* Lua function */
CallInfo *ci = next_ci(L); CallInfo *ci;
Proto *p = clLvalue(s2v(func))->p; Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */ int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams; int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */ int fsize = p->maxstacksize; /* frame size */
checkstackp(L, fsize, func); checkstackGCp(L, fsize, func);
L->ci = ci = next_ci(L);
ci->nresults = nresults; ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = 0;
ci->top = func + 1 + fsize; ci->top = func + 1 + fsize;
ci->func = func; ci->func = func;
L->ci = ci; L->ci = ci;
for (; narg < nfixparams; narg++) for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top++)); /* complete missing arguments */ setnilvalue(s2v(L->top++)); /* complete missing arguments */
lua_assert(ci->top <= L->stack_last); lua_assert(ci->top <= L->stack_last);
luaV_execute(L, ci); /* run the function */ return ci;
break;
} }
default: { /* not a function */ default: { /* not a function */
checkstackp(L, 1, func); /* space for metamethod */ checkstackGCp(L, 1, func); /* space for metamethod */
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
goto retry; /* try again with metamethod */ goto retry; /* try again with metamethod */
} }
@ -513,18 +533,37 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
} }
/*
** Call a function (C or Lua) through C. 'inc' can be 1 (increment
** number of recursive invocations in the C stack) or nyci (the same
** plus increment number of non-yieldable calls).
*/
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
CallInfo *ci;
L->nCcalls += inc;
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
luaV_execute(L, ci); /* call it */
}
L->nCcalls -= inc;
}
/*
** External interface for 'ccall'
*/
void luaD_call (lua_State *L, StkId func, int nResults) {
ccall(L, func, nResults, 1);
}
/* /*
** Similar to 'luaD_call', but does not allow yields during the call. ** Similar to 'luaD_call', but does not allow yields during the call.
** If there is a stack overflow, freeing all CI structures will
** force the subsequent call to invoke 'luaE_extendCI', which then
** will raise any errors.
*/ */
void luaD_callnoyield (lua_State *L, StkId func, int nResults) { void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
incXCcalls(L); ccall(L, func, nResults, nyci);
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */
luaE_freeCI(L);
luaD_call(L, func, nResults);
decXCcalls(L);
} }
@ -602,12 +641,12 @@ static int recover (lua_State *L, int status) {
if (ci == NULL) return 0; /* no recovery point */ if (ci == NULL) return 0; /* no recovery point */
/* "finish" luaD_pcall */ /* "finish" luaD_pcall */
oldtop = restorestack(L, ci->u2.funcidx); oldtop = restorestack(L, ci->u2.funcidx);
luaF_close(L, oldtop, status); /* may change the stack */
oldtop = restorestack(L, ci->u2.funcidx);
luaD_seterrorobj(L, status, oldtop);
L->ci = ci; L->ci = ci;
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
luaD_shrinkstack(L); status = luaF_close(L, oldtop, status); /* may change the stack */
oldtop = restorestack(L, ci->u2.funcidx);
luaD_seterrorobj(L, status, oldtop);
luaD_shrinkstack(L); /* restore stack size in case of overflow */
L->errfunc = ci->u.c.old_errfunc; L->errfunc = ci->u.c.old_errfunc;
return 1; /* continue running the coroutine */ return 1; /* continue running the coroutine */
} }
@ -638,12 +677,12 @@ static void resume (lua_State *L, void *ud) {
int n = *(cast(int*, ud)); /* number of arguments */ int n = *(cast(int*, ud)); /* number of arguments */
StkId firstArg = L->top - n; /* first argument */ StkId firstArg = L->top - n; /* first argument */
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
if (L->status == LUA_OK) { /* starting a coroutine? */ if (L->status == LUA_OK) /* starting a coroutine? */
luaD_call(L, firstArg - 1, LUA_MULTRET); ccall(L, firstArg - 1, LUA_MULTRET, 1); /* just call its body */
}
else { /* resuming from previous yield */ else { /* resuming from previous yield */
lua_assert(L->status == LUA_YIELD); lua_assert(L->status == LUA_YIELD);
L->status = LUA_OK; /* mark that it is running (again) */ L->status = LUA_OK; /* mark that it is running (again) */
luaE_incCstack(L); /* control the C stack */
if (isLua(ci)) /* yielded inside a hook? */ if (isLua(ci)) /* yielded inside a hook? */
luaV_execute(L, ci); /* just continue running Lua code */ luaV_execute(L, ci); /* just continue running Lua code */
else { /* 'common' yield */ else { /* 'common' yield */
@ -671,12 +710,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
} }
else if (L->status != LUA_YIELD) /* ended with errors? */ else if (L->status != LUA_YIELD) /* ended with errors? */
return resume_error(L, "cannot resume dead coroutine", nargs); return resume_error(L, "cannot resume dead coroutine", nargs);
if (from == NULL) L->nCcalls = (from) ? getCcalls(from) : 0;
L->nCcalls = CSTACKTHREAD;
else /* correct 'nCcalls' for this thread */
L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF;
if (L->nCcalls <= CSTACKERR)
return resume_error(L, "C stack overflow", nargs);
luai_userstateresume(L, nargs); luai_userstateresume(L, nargs);
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
status = luaD_rawrunprotected(L, resume, &nargs); status = luaD_rawrunprotected(L, resume, &nargs);
@ -706,9 +740,10 @@ LUA_API int lua_isyieldable (lua_State *L) {
LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
lua_KFunction k) { lua_KFunction k) {
CallInfo *ci = L->ci; CallInfo *ci;
luai_userstateyield(L, nresults); luai_userstateyield(L, nresults);
lua_lock(L); lua_lock(L);
ci = L->ci;
api_checknelems(L, nresults); api_checknelems(L, nresults);
if (unlikely(!yieldable(L))) { if (unlikely(!yieldable(L))) {
if (L != G(L)->mainthread) if (L != G(L)->mainthread)
@ -754,7 +789,7 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
status = luaF_close(L, oldtop, status); status = luaF_close(L, oldtop, status);
oldtop = restorestack(L, old_top); /* previous call may change stack */ oldtop = restorestack(L, old_top); /* previous call may change stack */
luaD_seterrorobj(L, status, oldtop); luaD_seterrorobj(L, status, oldtop);
luaD_shrinkstack(L); luaD_shrinkstack(L); /* restore stack size in case of overflow */
} }
L->errfunc = old_errfunc; L->errfunc = old_errfunc;
return status; return status;

View File

@ -17,6 +17,8 @@
** Macro to check stack size and grow stack if needed. Parameters ** Macro to check stack size and grow stack if needed. Parameters
** 'pre'/'pos' allow the macro to preserve a pointer into the ** 'pre'/'pos' allow the macro to preserve a pointer into the
** stack across reallocations, doing the work only when needed. ** stack across reallocations, doing the work only when needed.
** It also allows the running of one GC step when the stack is
** reallocated.
** 'condmovestack' is used in heavy tests to force a stack reallocation ** 'condmovestack' is used in heavy tests to force a stack reallocation
** at every check. ** at every check.
*/ */
@ -35,7 +37,7 @@
/* macro to check stack size, preserving 'p' */ /* macro to check stack size, preserving 'p' */
#define checkstackp(L,n,p) \ #define checkstackGCp(L,n,p) \
luaD_checkstackaux(L, n, \ luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
luaC_checkGC(L), /* stack grow uses memory */ \ luaC_checkGC(L), /* stack grow uses memory */ \
@ -44,7 +46,7 @@
/* macro to check stack size and GC */ /* macro to check stack size and GC */
#define checkstackGC(L,fsize) \ #define checkstackGC(L,fsize) \
luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L)) luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
/* type of protected functions, to be ran by 'runprotected' */ /* type of protected functions, to be ran by 'runprotected' */
@ -57,6 +59,7 @@ LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
int fTransfer, int nTransfer); int fTransfer, int nTransfer);
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n); LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);

View File

@ -53,7 +53,7 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
uv->v = &uv->u.value; /* make it closed */ uv->v = &uv->u.value; /* make it closed */
setnilvalue(uv->v); setnilvalue(uv->v);
cl->upvals[i] = uv; cl->upvals[i] = uv;
luaC_objbarrier(L, cl, o); luaC_objbarrier(L, cl, uv);
} }
} }
@ -234,10 +234,11 @@ int luaF_close (lua_State *L, StkId level, int status) {
luaF_unlinkupval(uv); luaF_unlinkupval(uv);
setobj(L, slot, uv->v); /* move value to upvalue slot */ setobj(L, slot, uv->v); /* move value to upvalue slot */
uv->v = slot; /* now current value lives here */ uv->v = slot; /* now current value lives here */
if (!iswhite(uv)) if (!iswhite(uv)) { /* neither white nor dead? */
gray2black(uv); /* closed upvalues cannot be gray */ nw2black(uv); /* closed upvalues cannot be gray */
luaC_barrier(L, uv, slot); luaC_barrier(L, uv, slot);
} }
}
return status; return status;
} }

View File

@ -60,16 +60,24 @@
#define PAUSEADJ 100 #define PAUSEADJ 100
/* mask to erase all color bits (plus gen. related stuff) */ /* mask with all color bits */
#define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS)) #define maskcolors (bitmask(BLACKBIT) | WHITEBITS)
/* mask with all GC bits */
#define maskgcbits (maskcolors | AGEBITS)
/* macro to erase all color bits then sets only the current white bit */ /* macro to erase all color bits then set only the current white bit */
#define makewhite(g,x) \ #define makewhite(g,x) \
(x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g))) (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g)))
#define white2gray(x) resetbits(x->marked, WHITEBITS) /* make an object gray (neither white nor black) */
#define black2gray(x) resetbit(x->marked, BLACKBIT) #define set2gray(x) resetbits(x->marked, maskcolors)
/* make an object black (coming from any color) */
#define set2black(x) \
(x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
@ -77,16 +85,13 @@
#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
#define checkconsistency(obj) \
lua_longassert(!iscollectable(obj) || righttt(obj))
/* /*
** Protected access to objects in values ** Protected access to objects in values
*/ */
#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) #define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL)
#define markvalue(g,o) { checkconsistency(o); \ #define markvalue(g,o) { checkliveness(g->mainthread,o); \
if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
@ -135,31 +140,38 @@ static GCObject **getgclist (GCObject *o) {
/* /*
** Link a collectable object 'o' with a known type into list pointed by 'p'. ** Link a collectable object 'o' with a known type into the list 'p'.
** (Must be a macro to access the 'gclist' field in different types.)
*/ */
#define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) #define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
lua_assert(!isgray(o)); /* cannot be in a gray list */
*pnext = *list;
*list = o;
set2gray(o); /* now it is */
}
/* /*
** Link a generic collectable object 'o' into list pointed by 'p'. ** Link a generic collectable object 'o' into the list 'p'.
*/ */
#define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o)) #define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
/* /*
** Clear keys for empty entries in tables. If entry is empty ** Clear keys for empty entries in tables. If entry is empty, mark its
** and its key is not marked, mark its entry as dead. This allows the ** entry as dead. This allows the collection of the key, but keeps its
** collection of the key, but keeps its entry in the table (its removal ** entry in the table: its removal could break a chain and could break
** could break a chain). The main feature of a dead key is that it must ** a table traversal. Other places never manipulate dead keys, because
** be different from any other value, to do not disturb searches. ** its associated empty value is enough to signal that the entry is
** Other places never manipulate dead keys, because its associated empty ** logically empty.
** value is enough to signal that the entry is logically empty.
*/ */
static void clearkey (Node *n) { static void clearkey (Node *n) {
lua_assert(isempty(gval(n))); lua_assert(isempty(gval(n)));
if (keyiswhite(n)) if (keyiscollectable(n))
setdeadkey(n); /* unused and unmarked key; remove it */ setdeadkey(n); /* unused key; remove it */
} }
@ -181,14 +193,17 @@ static int iscleared (global_State *g, const GCObject *o) {
/* /*
** barrier that moves collector forward, that is, mark the white object ** Barrier that moves collector forward, that is, marks the white object
** 'v' being pointed by the black object 'o'. (If in sweep phase, clear ** 'v' being pointed by the black object 'o'. In the generational
** the black object to white [sweep it] to avoid other barrier calls for ** mode, 'v' must also become old, if 'o' is old; however, it cannot
** this same object.) In the generational mode, 'v' must also become ** be changed directly to OLD, because it may still point to non-old
** old, if 'o' is old; however, it cannot be changed directly to OLD, ** objects. So, it is marked as OLD0. In the next cycle it will become
** because it may still point to non-old objects. So, it is marked as ** OLD1, and in the next it will finally become OLD (regular old). By
** OLD0. In the next cycle it will become OLD1, and in the next it ** then, any object it points to will also be old. If called in the
** will finally become OLD (regular old). ** incremental sweep phase, it clears the black object to white (sweep
** it) to avoid other barrier calls for this same object. (That cannot
** be done is generational mode, as its sweep does not distinguish
** whites from deads.)
*/ */
void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
global_State *g = G(L); global_State *g = G(L);
@ -202,7 +217,8 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
} }
else { /* sweep phase */ else { /* sweep phase */
lua_assert(issweepphase(g)); lua_assert(issweepphase(g));
makewhite(g, o); /* mark main obj. as white to avoid other barriers */ if (g->gckind == KGC_INC) /* incremental mode? */
makewhite(g, o); /* mark 'o' as white to avoid other barriers */
} }
} }
@ -214,10 +230,12 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
void luaC_barrierback_ (lua_State *L, GCObject *o) { void luaC_barrierback_ (lua_State *L, GCObject *o) {
global_State *g = G(L); global_State *g = G(L);
lua_assert(isblack(o) && !isdead(g, o)); lua_assert(isblack(o) && !isdead(g, o));
lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1)); lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
if (getage(o) != G_TOUCHED2) /* not already in gray list? */ if (getage(o) == G_TOUCHED2) /* already in gray list? */
linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */ set2gray(o); /* make it gray to become touched1 */
black2gray(o); /* make object gray (again) */ else /* link it in 'grayagain' and paint it gray */
linkobjgclist(o, g->grayagain);
if (isold(o)) /* generational mode? */
setage(o, G_TOUCHED1); /* touched in current cycle */ setage(o, G_TOUCHED1); /* touched in current cycle */
} }
@ -225,7 +243,7 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) {
void luaC_fix (lua_State *L, GCObject *o) { void luaC_fix (lua_State *L, GCObject *o) {
global_State *g = G(L); global_State *g = G(L);
lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */
white2gray(o); /* they will be gray forever */ set2gray(o); /* they will be gray forever */
setage(o, G_OLD); /* and old forever */ setage(o, G_OLD); /* and old forever */
g->allgc = o->next; /* remove object from 'allgc' list */ g->allgc = o->next; /* remove object from 'allgc' list */
o->next = g->fixedgc; /* link it to 'fixedgc' list */ o->next = g->fixedgc; /* link it to 'fixedgc' list */
@ -259,24 +277,30 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
/* /*
** Mark an object. Userdata, strings, and closed upvalues are visited ** Mark an object. Userdata with no user values, strings, and closed
** and turned black here. Other objects are marked gray and added ** upvalues are visited and turned black here. Open upvalues are
** to appropriate list to be visited (and turned black) later. (Open ** already indirectly linked through their respective threads in the
** upvalues are already linked in 'headuv' list. They are kept gray ** 'twups' list, so they don't go to the gray list; nevertheless, they
** to avoid barriers, as their values will be revisited by the thread.) ** are kept gray to avoid barriers, as their values will be revisited
** by the thread or by 'remarkupvals'. Other objects are added to the
** gray list to be visited (and turned black) later. Both userdata and
** upvalues can call this function recursively, but this recursion goes
** for at most two levels: An upvalue cannot refer to another upvalue
** (only closures can), and a userdata's metatable must be a table.
*/ */
static void reallymarkobject (global_State *g, GCObject *o) { static void reallymarkobject (global_State *g, GCObject *o) {
white2gray(o);
switch (o->tt) { switch (o->tt) {
case LUA_VSHRSTR: case LUA_VSHRSTR:
case LUA_VLNGSTR: { case LUA_VLNGSTR: {
gray2black(o); set2black(o); /* nothing to visit */
break; break;
} }
case LUA_VUPVAL: { case LUA_VUPVAL: {
UpVal *uv = gco2upv(o); UpVal *uv = gco2upv(o);
if (!upisopen(uv)) /* open upvalues are kept gray */ if (upisopen(uv))
gray2black(o); set2gray(uv); /* open upvalues are kept gray */
else
set2black(uv); /* closed upvalues are visited here */
markvalue(g, uv->v); /* mark its content */ markvalue(g, uv->v); /* mark its content */
break; break;
} }
@ -284,14 +308,14 @@ static void reallymarkobject (global_State *g, GCObject *o) {
Udata *u = gco2u(o); Udata *u = gco2u(o);
if (u->nuvalue == 0) { /* no user values? */ if (u->nuvalue == 0) { /* no user values? */
markobjectN(g, u->metatable); /* mark its metatable */ markobjectN(g, u->metatable); /* mark its metatable */
gray2black(o); /* nothing else to mark */ set2black(u); /* nothing else to mark */
break; break;
} }
/* else... */ /* else... */
} /* FALLTHROUGH */ } /* FALLTHROUGH */
case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE:
case LUA_VTHREAD: case LUA_VPROTO: { case LUA_VTHREAD: case LUA_VPROTO: {
linkobjgclist(o, g->gray); linkobjgclist(o, g->gray); /* to be visited later */
break; break;
} }
default: lua_assert(0); break; default: lua_assert(0); break;
@ -324,41 +348,54 @@ static lu_mem markbeingfnz (global_State *g) {
/* /*
** Mark all values stored in marked open upvalues from non-marked threads. ** For each non-marked thread, simulates a barrier between each open
** (Values from marked threads were already marked when traversing the ** upvalue and its value. (If the thread is collected, the value will be
** thread.) Remove from the list threads that no longer have upvalues and ** assigned to the upvalue, but then it can be too late for the barrier
** not-marked threads. ** to act. The "barrier" does not need to check colors: A non-marked
** thread must be young; upvalues cannot be older than their threads; so
** any visited upvalue must be young too.) Also removes the thread from
** the list, as it was already visited. Removes also threads with no
** upvalues, as they have nothing to be checked. (If the thread gets an
** upvalue later, it will be linked in the list again.)
*/ */
static int remarkupvals (global_State *g) { static int remarkupvals (global_State *g) {
lua_State *thread; lua_State *thread;
lua_State **p = &g->twups; lua_State **p = &g->twups;
int work = 0; int work = 0; /* estimate of how much work was done here */
while ((thread = *p) != NULL) { while ((thread = *p) != NULL) {
work++; work++;
lua_assert(!isblack(thread)); /* threads are never black */ if (!iswhite(thread) && thread->openupval != NULL)
if (isgray(thread) && thread->openupval != NULL)
p = &thread->twups; /* keep marked thread with upvalues in the list */ p = &thread->twups; /* keep marked thread with upvalues in the list */
else { /* thread is not marked or without upvalues */ else { /* thread is not marked or without upvalues */
UpVal *uv; UpVal *uv;
lua_assert(!isold(thread) || thread->openupval == NULL);
*p = thread->twups; /* remove thread from the list */ *p = thread->twups; /* remove thread from the list */
thread->twups = thread; /* mark that it is out of list */ thread->twups = thread; /* mark that it is out of list */
for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
lua_assert(getage(uv) <= getage(thread));
work++; work++;
if (!iswhite(uv)) /* upvalue already visited? */ if (!iswhite(uv)) { /* upvalue already visited? */
lua_assert(upisopen(uv) && isgray(uv));
markvalue(g, uv->v); /* mark its value */ markvalue(g, uv->v); /* mark its value */
} }
} }
} }
}
return work; return work;
} }
static void cleargraylists (global_State *g) {
g->gray = g->grayagain = NULL;
g->weak = g->allweak = g->ephemeron = NULL;
}
/* /*
** mark root set and reset all gray lists, to start a new collection ** mark root set and reset all gray lists, to start a new collection
*/ */
static void restartcollection (global_State *g) { static void restartcollection (global_State *g) {
g->gray = g->grayagain = NULL; cleargraylists(g);
g->weak = g->allweak = g->ephemeron = NULL;
markobject(g, g->mainthread); markobject(g, g->mainthread);
markvalue(g, &g->l_registry); markvalue(g, &g->l_registry);
markmt(g); markmt(g);
@ -374,6 +411,26 @@ static void restartcollection (global_State *g) {
** ======================================================= ** =======================================================
*/ */
/*
** Check whether object 'o' should be kept in the 'grayagain' list for
** post-processing by 'correctgraylist'. (It could put all old objects
** in the list and leave all the work to 'correctgraylist', but it is
** more efficient to avoid adding elements that will be removed.) Only
** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
** back to a gray list, but then it must become OLD. (That is what
** 'correctgraylist' does when it finds a TOUCHED2 object.)
*/
static void genlink (global_State *g, GCObject *o) {
lua_assert(isblack(o));
if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
} /* everything else do not need to be linked back */
else if (getage(o) == G_TOUCHED2)
changeage(o, G_TOUCHED2, G_OLD); /* advance age */
}
/* /*
** Traverse a table with weak values and link it to proper list. During ** Traverse a table with weak values and link it to proper list. During
** propagate phase, keep it in 'grayagain' list, to be revisited in the ** propagate phase, keep it in 'grayagain' list, to be revisited in the
@ -410,8 +467,9 @@ static void traverseweakvalue (global_State *g, Table *h) {
** the atomic phase, if table has any white->white entry, it has to ** the atomic phase, if table has any white->white entry, it has to
** be revisited during ephemeron convergence (as that key may turn ** be revisited during ephemeron convergence (as that key may turn
** black). Otherwise, if it has any white key, table has to be cleared ** black). Otherwise, if it has any white key, table has to be cleared
** (in the atomic phase). In generational mode, it (like all visited ** (in the atomic phase). In generational mode, some tables
** tables) must be kept in some gray list for post-processing. ** must be kept in some gray list for post-processing; this is done
** by 'genlink'.
*/ */
static int traverseephemeron (global_State *g, Table *h, int inv) { static int traverseephemeron (global_State *g, Table *h, int inv) {
int marked = 0; /* true if an object is marked in this traversal */ int marked = 0; /* true if an object is marked in this traversal */
@ -450,10 +508,8 @@ static int traverseephemeron (global_State *g, Table *h, int inv) {
linkgclist(h, g->ephemeron); /* have to propagate again */ linkgclist(h, g->ephemeron); /* have to propagate again */
else if (hasclears) /* table has white keys? */ else if (hasclears) /* table has white keys? */
linkgclist(h, g->allweak); /* may have to clean white keys */ linkgclist(h, g->allweak); /* may have to clean white keys */
else if (g->gckind == KGC_GEN)
linkgclist(h, g->grayagain); /* keep it in some list */
else else
gray2black(h); genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
return marked; return marked;
} }
@ -473,10 +529,7 @@ static void traversestrongtable (global_State *g, Table *h) {
markvalue(g, gval(n)); markvalue(g, gval(n));
} }
} }
if (g->gckind == KGC_GEN) { genlink(g, obj2gco(h));
linkgclist(h, g->grayagain); /* keep it in some gray list */
black2gray(h);
}
} }
@ -488,7 +541,6 @@ static lu_mem traversetable (global_State *g, Table *h) {
(cast_void(weakkey = strchr(svalue(mode), 'k')), (cast_void(weakkey = strchr(svalue(mode), 'k')),
cast_void(weakvalue = strchr(svalue(mode), 'v')), cast_void(weakvalue = strchr(svalue(mode), 'v')),
(weakkey || weakvalue))) { /* is really weak? */ (weakkey || weakvalue))) { /* is really weak? */
black2gray(h); /* keep table gray */
if (!weakkey) /* strong keys? */ if (!weakkey) /* strong keys? */
traverseweakvalue(g, h); traverseweakvalue(g, h);
else if (!weakvalue) /* strong values? */ else if (!weakvalue) /* strong values? */
@ -507,10 +559,7 @@ static int traverseudata (global_State *g, Udata *u) {
markobjectN(g, u->metatable); /* mark its metatable */ markobjectN(g, u->metatable); /* mark its metatable */
for (i = 0; i < u->nuvalue; i++) for (i = 0; i < u->nuvalue; i++)
markvalue(g, &u->uv[i].uv); markvalue(g, &u->uv[i].uv);
if (g->gckind == KGC_GEN) { genlink(g, obj2gco(u));
linkgclist(u, g->grayagain); /* keep it in some gray list */
black2gray(u);
}
return 1 + u->nuvalue; return 1 + u->nuvalue;
} }
@ -559,12 +608,21 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
/* /*
** Traverse a thread, marking the elements in the stack up to its top ** Traverse a thread, marking the elements in the stack up to its top
** and cleaning the rest of the stack in the final traversal. ** and cleaning the rest of the stack in the final traversal. That
** That ensures that the entire stack have valid (non-dead) objects. ** ensures that the entire stack have valid (non-dead) objects.
** Threads have no barriers. In gen. mode, old threads must be visited
** at every cycle, because they might point to young objects. In inc.
** mode, the thread can still be modified before the end of the cycle,
** and therefore it must be visited again in the atomic phase. To ensure
** these visits, threads must return to a gray list if they are not new
** (which can only happen in generational mode) or if the traverse is in
** the propagate phase (which can only happen in incremental mode).
*/ */
static int traversethread (global_State *g, lua_State *th) { static int traversethread (global_State *g, lua_State *th) {
UpVal *uv; UpVal *uv;
StkId o = th->stack; StkId o = th->stack;
if (isold(th) || g->gcstate == GCSpropagate)
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
if (o == NULL) if (o == NULL)
return 1; /* stack not completely built yet */ return 1; /* stack not completely built yet */
lua_assert(g->gcstate == GCSatomic || lua_assert(g->gcstate == GCSatomic ||
@ -574,9 +632,8 @@ static int traversethread (global_State *g, lua_State *th) {
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markobject(g, uv); /* open upvalues cannot be collected */ markobject(g, uv); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */ if (g->gcstate == GCSatomic) { /* final traversal? */
StkId lim = th->stack + th->stacksize; /* real end of stack */ for (; o < th->stack_last + EXTRA_STACK; o++)
for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(s2v(o)); /* clear dead stack slice */
setnilvalue(s2v(o));
/* 'remarkupvals' may have removed thread from 'twups' list */ /* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) { if (!isintwups(th) && th->openupval != NULL) {
th->twups = g->twups; /* link it back to the list */ th->twups = g->twups; /* link it back to the list */
@ -585,17 +642,16 @@ static int traversethread (global_State *g, lua_State *th) {
} }
else if (!g->gcemergency) else if (!g->gcemergency)
luaD_shrinkstack(th); /* do not change stack in emergency cycle */ luaD_shrinkstack(th); /* do not change stack in emergency cycle */
return 1 + th->stacksize; return 1 + stacksize(th);
} }
/* /*
** traverse one gray object, turning it to black (except for threads, ** traverse one gray object, turning it to black.
** which are always gray).
*/ */
static lu_mem propagatemark (global_State *g) { static lu_mem propagatemark (global_State *g) {
GCObject *o = g->gray; GCObject *o = g->gray;
gray2black(o); nw2black(o);
g->gray = *getgclist(o); /* remove from 'gray' list */ g->gray = *getgclist(o); /* remove from 'gray' list */
switch (o->tt) { switch (o->tt) {
case LUA_VTABLE: return traversetable(g, gco2t(o)); case LUA_VTABLE: return traversetable(g, gco2t(o));
@ -603,12 +659,7 @@ static lu_mem propagatemark (global_State *g) {
case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
case LUA_VPROTO: return traverseproto(g, gco2p(o)); case LUA_VPROTO: return traverseproto(g, gco2p(o));
case LUA_VTHREAD: { case LUA_VTHREAD: return traversethread(g, gco2th(o));
lua_State *th = gco2th(o);
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
black2gray(o);
return traversethread(g, th);
}
default: lua_assert(0); return 0; default: lua_assert(0); return 0;
} }
} }
@ -638,8 +689,10 @@ static void convergeephemerons (global_State *g) {
g->ephemeron = NULL; /* tables may return to this list when traversed */ g->ephemeron = NULL; /* tables may return to this list when traversed */
changed = 0; changed = 0;
while ((w = next) != NULL) { /* for each ephemeron table */ while ((w = next) != NULL) { /* for each ephemeron table */
next = gco2t(w)->gclist; /* list is rebuilt during loop */ Table *h = gco2t(w);
if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */ next = h->gclist; /* list is rebuilt during loop */
nw2black(h); /* out of the list (for now) */
if (traverseephemeron(g, h, dir)) { /* marked some value? */
propagateall(g); /* propagate changes */ propagateall(g); /* propagate changes */
changed = 1; /* will have to revisit all ephemeron tables */ changed = 1; /* will have to revisit all ephemeron tables */
} }
@ -716,12 +769,16 @@ static void freeobj (lua_State *L, GCObject *o) {
case LUA_VUPVAL: case LUA_VUPVAL:
freeupval(L, gco2upv(o)); freeupval(L, gco2upv(o));
break; break;
case LUA_VLCL: case LUA_VLCL: {
luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); LClosure *cl = gco2lcl(o);
luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
break; break;
case LUA_VCCL: }
luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); case LUA_VCCL: {
CClosure *cl = gco2ccl(o);
luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
break; break;
}
case LUA_VTABLE: case LUA_VTABLE:
luaH_free(L, gco2t(o)); luaH_free(L, gco2t(o));
break; break;
@ -733,13 +790,17 @@ static void freeobj (lua_State *L, GCObject *o) {
luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
break; break;
} }
case LUA_VSHRSTR: case LUA_VSHRSTR: {
luaS_remove(L, gco2ts(o)); /* remove it from hash table */ TString *ts = gco2ts(o);
luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); luaS_remove(L, ts); /* remove it from hash table */
luaM_freemem(L, ts, sizelstring(ts->shrlen));
break; break;
case LUA_VLNGSTR: }
luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); case LUA_VLNGSTR: {
TString *ts = gco2ts(o);
luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
break; break;
}
default: lua_assert(0); default: lua_assert(0);
} }
} }
@ -766,7 +827,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin,
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { /* change mark to 'white' */ else { /* change mark to 'white' */
curr->marked = cast_byte((marked & maskcolors) | white); curr->marked = cast_byte((marked & ~maskgcbits) | white);
p = &curr->next; /* go to next element */ p = &curr->next; /* go to next element */
} }
} }
@ -823,6 +884,8 @@ static GCObject *udata2finalize (global_State *g) {
resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
if (issweepphase(g)) if (issweepphase(g))
makewhite(g, o); /* "sweep" object */ makewhite(g, o); /* "sweep" object */
else if (getage(o) == G_OLD1)
g->firstold1 = o; /* it is the first OLD1 object in the list */
return o; return o;
} }
@ -896,15 +959,15 @@ static GCObject **findlast (GCObject **p) {
/* /*
** Move all unreachable objects (or 'all' objects) that need ** Move all unreachable objects (or 'all' objects) that need
** finalization from list 'finobj' to list 'tobefnz' (to be finalized). ** finalization from list 'finobj' to list 'tobefnz' (to be finalized).
** (Note that objects after 'finobjold' cannot be white, so they ** (Note that objects after 'finobjold1' cannot be white, so they
** don't need to be traversed. In incremental mode, 'finobjold' is NULL, ** don't need to be traversed. In incremental mode, 'finobjold1' is NULL,
** so the whole list is traversed.) ** so the whole list is traversed.)
*/ */
static void separatetobefnz (global_State *g, int all) { static void separatetobefnz (global_State *g, int all) {
GCObject *curr; GCObject *curr;
GCObject **p = &g->finobj; GCObject **p = &g->finobj;
GCObject **lastnext = findlast(&g->tobefnz); GCObject **lastnext = findlast(&g->tobefnz);
while ((curr = *p) != g->finobjold) { /* traverse all finalizable objects */ while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */
lua_assert(tofinalize(curr)); lua_assert(tofinalize(curr));
if (!(iswhite(curr) || all)) /* not being collected? */ if (!(iswhite(curr) || all)) /* not being collected? */
p = &curr->next; /* don't bother with it */ p = &curr->next; /* don't bother with it */
@ -920,6 +983,27 @@ static void separatetobefnz (global_State *g, int all) {
} }
/*
** If pointer 'p' points to 'o', move it to the next element.
*/
static void checkpointer (GCObject **p, GCObject *o) {
if (o == *p)
*p = o->next;
}
/*
** Correct pointers to objects inside 'allgc' list when
** object 'o' is being removed from the list.
*/
static void correctpointers (global_State *g, GCObject *o) {
checkpointer(&g->survival, o);
checkpointer(&g->old1, o);
checkpointer(&g->reallyold, o);
checkpointer(&g->firstold1, o);
}
/* /*
** if object 'o' has a finalizer, remove it from 'allgc' list (must ** if object 'o' has a finalizer, remove it from 'allgc' list (must
** search the list to find it) and link it in 'finobj' list. ** search the list to find it) and link it in 'finobj' list.
@ -936,14 +1020,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
} }
else { /* correct pointers into 'allgc' list, if needed */ else
if (o == g->survival) correctpointers(g, o);
g->survival = o->next;
if (o == g->old)
g->old = o->next;
if (o == g->reallyold)
g->reallyold = o->next;
}
/* search for pointer pointing to 'o' */ /* search for pointer pointing to 'o' */
for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
*p = o->next; /* remove 'o' from 'allgc' list */ *p = o->next; /* remove 'o' from 'allgc' list */
@ -965,24 +1043,31 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
static void setpause (global_State *g); static void setpause (global_State *g);
/* mask to erase all color bits, not changing gen-related stuff */
#define maskgencolors (~(bitmask(BLACKBIT) | WHITEBITS))
/* /*
** Sweep a list of objects, deleting dead ones and turning ** Sweep a list of objects to enter generational mode. Deletes dead
** the non dead to old (without changing their colors). ** objects and turns the non dead to old. All non-dead threads---which
** are now old---must be in a gray list. Everything else is not in a
** gray list. Open upvalues are also kept gray.
*/ */
static void sweep2old (lua_State *L, GCObject **p) { static void sweep2old (lua_State *L, GCObject **p) {
GCObject *curr; GCObject *curr;
global_State *g = G(L);
while ((curr = *p) != NULL) { while ((curr = *p) != NULL) {
if (iswhite(curr)) { /* is 'curr' dead? */ if (iswhite(curr)) { /* is 'curr' dead? */
lua_assert(isdead(G(L), curr)); lua_assert(isdead(g, curr));
*p = curr->next; /* remove 'curr' from list */ *p = curr->next; /* remove 'curr' from list */
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { /* all surviving objects become old */ else { /* all surviving objects become old */
setage(curr, G_OLD); setage(curr, G_OLD);
if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
lua_State *th = gco2th(curr);
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
}
else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
set2gray(curr); /* open upvalues are always gray */
else /* everything else is black */
nw2black(curr);
p = &curr->next; /* go to next element */ p = &curr->next; /* go to next element */
} }
} }
@ -995,9 +1080,13 @@ static void sweep2old (lua_State *L, GCObject **p) {
** during the sweep. So, any white object must be dead.) For ** during the sweep. So, any white object must be dead.) For
** non-dead objects, advance their ages and clear the color of ** non-dead objects, advance their ages and clear the color of
** new objects. (Old objects keep their colors.) ** new objects. (Old objects keep their colors.)
** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced
** here, because these old-generation objects are usually not swept
** here. They will all be advanced in 'correctgraylist'. That function
** will also remove objects turned white here from any gray list.
*/ */
static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
GCObject *limit) { GCObject *limit, GCObject **pfirstold1) {
static const lu_byte nextage[] = { static const lu_byte nextage[] = {
G_SURVIVAL, /* from G_NEW */ G_SURVIVAL, /* from G_NEW */
G_OLD1, /* from G_SURVIVAL */ G_OLD1, /* from G_SURVIVAL */
@ -1016,9 +1105,15 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { /* correct mark and age */ else { /* correct mark and age */
if (getage(curr) == G_NEW) if (getage(curr) == G_NEW) { /* new objects go back to white */
curr->marked = cast_byte((curr->marked & maskgencolors) | white); int marked = curr->marked & ~maskgcbits; /* erase GC bits */
curr->marked = cast_byte(marked | G_SURVIVAL | white);
}
else { /* all other objects will be old, and so keep their color */
setage(curr, nextage[getage(curr)]); setage(curr, nextage[getage(curr)]);
if (getage(curr) == G_OLD1 && *pfirstold1 == NULL)
*pfirstold1 = curr; /* first OLD1 object in the list */
}
p = &curr->next; /* go to next element */ p = &curr->next; /* go to next element */
} }
} }
@ -1028,58 +1123,50 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
/* /*
** Traverse a list making all its elements white and clearing their ** Traverse a list making all its elements white and clearing their
** age. ** age. In incremental mode, all objects are 'new' all the time,
** except for fixed strings (which are always old).
*/ */
static void whitelist (global_State *g, GCObject *p) { static void whitelist (global_State *g, GCObject *p) {
int white = luaC_white(g); int white = luaC_white(g);
for (; p != NULL; p = p->next) for (; p != NULL; p = p->next)
p->marked = cast_byte((p->marked & maskcolors) | white); p->marked = cast_byte((p->marked & ~maskgcbits) | white);
} }
/* /*
** Correct a list of gray objects. ** Correct a list of gray objects. Return pointer to where rest of the
** list should be linked.
** Because this correction is done after sweeping, young objects might ** Because this correction is done after sweeping, young objects might
** be turned white and still be in the list. They are only removed. ** be turned white and still be in the list. They are only removed.
** For tables and userdata, advance 'touched1' to 'touched2'; 'touched2' ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list;
** objects become regular old and are removed from the list. ** Non-white threads also remain on the list; 'TOUCHED2' objects become
** For threads, just remove white ones from the list. ** regular old; they and anything else are removed from the list.
*/ */
static GCObject **correctgraylist (GCObject **p) { static GCObject **correctgraylist (GCObject **p) {
GCObject *curr; GCObject *curr;
while ((curr = *p) != NULL) { while ((curr = *p) != NULL) {
switch (curr->tt) {
case LUA_VTABLE: case LUA_VUSERDATA: {
GCObject **next = getgclist(curr); GCObject **next = getgclist(curr);
if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ if (iswhite(curr))
goto remove; /* remove all white objects */
else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
lua_assert(isgray(curr)); lua_assert(isgray(curr));
gray2black(curr); /* make it black, for next barrier */ nw2black(curr); /* make it black, for next barrier */
changeage(curr, G_TOUCHED1, G_TOUCHED2); changeage(curr, G_TOUCHED1, G_TOUCHED2);
p = next; /* go to next element */ goto remain; /* keep it in the list and go to next element */
} }
else { /* not touched in this cycle */ else if (curr->tt == LUA_VTHREAD) {
if (!iswhite(curr)) { /* not white? */ lua_assert(isgray(curr));
lua_assert(isold(curr)); goto remain; /* keep non-white threads on the list */
if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */
changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */
gray2black(curr); /* make it black */
} }
/* else, object is white: just remove it from this list */ else { /* everything else is removed */
*p = *next; /* remove 'curr' from gray list */ lua_assert(isold(curr)); /* young objects should be white here */
} if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */
break; changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */
} nw2black(curr); /* make object black (to be removed) */
case LUA_VTHREAD: { goto remove;
lua_State *th = gco2th(curr);
lua_assert(!isblack(th));
if (iswhite(th)) /* new object? */
*p = th->gclist; /* remove from gray list */
else /* old threads remain gray */
p = &th->gclist; /* go to next element */
break;
}
default: lua_assert(0); /* nothing more could be gray here */
} }
remove: *p = *next; continue;
remain: p = next; continue;
} }
return p; return p;
} }
@ -1100,7 +1187,7 @@ static void correctgraylists (global_State *g) {
/* /*
** Mark 'OLD1' objects when starting a new young collection. ** Mark black 'OLD1' objects when starting a new young collection.
** Gray objects are already in some gray list, and so will be visited ** Gray objects are already in some gray list, and so will be visited
** in the atomic step. ** in the atomic step.
*/ */
@ -1109,13 +1196,12 @@ static void markold (global_State *g, GCObject *from, GCObject *to) {
for (p = from; p != to; p = p->next) { for (p = from; p != to; p = p->next) {
if (getage(p) == G_OLD1) { if (getage(p) == G_OLD1) {
lua_assert(!iswhite(p)); lua_assert(!iswhite(p));
if (isblack(p)) { changeage(p, G_OLD1, G_OLD); /* now they are old */
black2gray(p); /* should be '2white', but gray works too */ if (isblack(p))
reallymarkobject(g, p); reallymarkobject(g, p);
} }
} }
} }
}
/* /*
@ -1131,50 +1217,63 @@ static void finishgencycle (lua_State *L, global_State *g) {
/* /*
** Does a young collection. First, mark 'OLD1' objects. (Only survival ** Does a young collection. First, mark 'OLD1' objects. Then does the
** and "recent old" lists can contain 'OLD1' objects. New lists cannot ** atomic step. Then, sweep all lists and advance pointers. Finally,
** contain 'OLD1' objects, at most 'OLD0' objects that were already ** finish the collection.
** visited when marked old.) Then does the atomic step. Then,
** sweep all lists and advance pointers. Finally, finish the collection.
*/ */
static void youngcollection (lua_State *L, global_State *g) { static void youngcollection (lua_State *L, global_State *g) {
GCObject **psurvival; /* to point to first non-dead survival object */ GCObject **psurvival; /* to point to first non-dead survival object */
GCObject *dummy; /* dummy out parameter to 'sweepgen' */
lua_assert(g->gcstate == GCSpropagate); lua_assert(g->gcstate == GCSpropagate);
markold(g, g->survival, g->reallyold); if (g->firstold1) { /* are there regular OLD1 objects? */
markold(g, g->firstold1, g->reallyold); /* mark them */
g->firstold1 = NULL; /* no more OLD1 objects (for now) */
}
markold(g, g->finobj, g->finobjrold); markold(g, g->finobj, g->finobjrold);
markold(g, g->tobefnz, NULL);
atomic(L); atomic(L);
/* sweep nursery and get a pointer to its last live element */ /* sweep nursery and get a pointer to its last live element */
psurvival = sweepgen(L, g, &g->allgc, g->survival); g->gcstate = GCSswpallgc;
/* sweep 'survival' and 'old' */ psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
sweepgen(L, g, psurvival, g->reallyold); /* sweep 'survival' */
g->reallyold = g->old; sweepgen(L, g, psurvival, g->old1, &g->firstold1);
g->old = *psurvival; /* 'survival' survivals are old now */ g->reallyold = g->old1;
g->old1 = *psurvival; /* 'survival' survivals are old now */
g->survival = g->allgc; /* all news are survivals */ g->survival = g->allgc; /* all news are survivals */
/* repeat for 'finobj' lists */ /* repeat for 'finobj' lists */
psurvival = sweepgen(L, g, &g->finobj, g->finobjsur); dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
/* sweep 'survival' and 'old' */ psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
sweepgen(L, g, psurvival, g->finobjrold); /* sweep 'survival' */
g->finobjrold = g->finobjold; sweepgen(L, g, psurvival, g->finobjold1, &dummy);
g->finobjold = *psurvival; /* 'survival' survivals are old now */ g->finobjrold = g->finobjold1;
g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
g->finobjsur = g->finobj; /* all news are survivals */ g->finobjsur = g->finobj; /* all news are survivals */
sweepgen(L, g, &g->tobefnz, NULL); sweepgen(L, g, &g->tobefnz, NULL, &dummy);
finishgencycle(L, g); finishgencycle(L, g);
} }
/*
** Clears all gray lists, sweeps objects, and prepare sublists to enter
** generational mode. The sweeps remove dead objects and turn all
** surviving objects to old. Threads go back to 'grayagain'; everything
** else is turned black (not in any gray list).
*/
static void atomic2gen (lua_State *L, global_State *g) { static void atomic2gen (lua_State *L, global_State *g) {
cleargraylists(g);
/* sweep all elements making them old */ /* sweep all elements making them old */
g->gcstate = GCSswpallgc;
sweep2old(L, &g->allgc); sweep2old(L, &g->allgc);
/* everything alive now is old */ /* everything alive now is old */
g->reallyold = g->old = g->survival = g->allgc; g->reallyold = g->old1 = g->survival = g->allgc;
g->firstold1 = NULL; /* there are no OLD1 objects anywhere */
/* repeat for 'finobj' lists */ /* repeat for 'finobj' lists */
sweep2old(L, &g->finobj); sweep2old(L, &g->finobj);
g->finobjrold = g->finobjold = g->finobjsur = g->finobj; g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj;
sweep2old(L, &g->tobefnz); sweep2old(L, &g->tobefnz);
@ -1187,8 +1286,9 @@ static void atomic2gen (lua_State *L, global_State *g) {
/* /*
** Enter generational mode. Must go until the end of an atomic cycle ** Enter generational mode. Must go until the end of an atomic cycle
** to ensure that all threads and weak tables are in the gray lists. ** to ensure that all objects are correctly marked and weak tables
** Then, turn all objects into old and finishes the collection. ** are cleared. Then, turn all objects into old and finishes the
** collection.
*/ */
static lu_mem entergen (lua_State *L, global_State *g) { static lu_mem entergen (lua_State *L, global_State *g) {
lu_mem numobjs; lu_mem numobjs;
@ -1207,10 +1307,10 @@ static lu_mem entergen (lua_State *L, global_State *g) {
*/ */
static void enterinc (global_State *g) { static void enterinc (global_State *g) {
whitelist(g, g->allgc); whitelist(g, g->allgc);
g->reallyold = g->old = g->survival = NULL; g->reallyold = g->old1 = g->survival = NULL;
whitelist(g, g->finobj); whitelist(g, g->finobj);
whitelist(g, g->tobefnz); whitelist(g, g->tobefnz);
g->finobjrold = g->finobjold = g->finobjsur = NULL; g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
g->gcstate = GCSpause; g->gcstate = GCSpause;
g->gckind = KGC_INC; g->gckind = KGC_INC;
g->lastatomic = 0; g->lastatomic = 0;

View File

@ -12,16 +12,16 @@
#include "lstate.h" #include "lstate.h"
/* /*
** Collectable objects may have one of three colors: white, which ** Collectable objects may have one of three colors: white, which means
** means the object is not marked; gray, which means the ** the object is not marked; gray, which means the object is marked, but
** object is marked, but its references may be not marked; and ** its references may be not marked; and black, which means that the
** black, which means that the object and all its references are marked. ** object and all its references are marked. The main invariant of the
** The main invariant of the garbage collector, while marking objects, ** garbage collector, while marking objects, is that a black object can
** is that a black object can never point to a white one. Moreover, ** never point to a white one. Moreover, any gray object must be in a
** any gray object must be in a "gray list" (gray, grayagain, weak, ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
** allweak, ephemeron) so that it can be visited again before finishing ** can be visited again before finishing the collection cycle. (Open
** the collection cycle. These lists have no meaning when the invariant ** upvalues are an exception to this rule.) These lists have no meaning
** is not being enforced (e.g., sweep phase). ** when the invariant is not being enforced (e.g., sweep phase).
*/ */
@ -69,14 +69,16 @@
/* /*
** Layout for bit use in 'marked' field. First three bits are ** Layout for bit use in 'marked' field. First three bits are
** used for object "age" in generational mode. Last bit is free ** used for object "age" in generational mode. Last bit is used
** to be used by respective objects. ** by tests.
*/ */
#define WHITE0BIT 3 /* object is white (type 0) */ #define WHITE0BIT 3 /* object is white (type 0) */
#define WHITE1BIT 4 /* object is white (type 1) */ #define WHITE1BIT 4 /* object is white (type 1) */
#define BLACKBIT 5 /* object is black */ #define BLACKBIT 5 /* object is black */
#define FINALIZEDBIT 6 /* object has been marked for finalization */ #define FINALIZEDBIT 6 /* object has been marked for finalization */
#define TESTBIT 7
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
@ -94,7 +96,8 @@
#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) #define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
#define changewhite(x) ((x)->marked ^= WHITEBITS) #define changewhite(x) ((x)->marked ^= WHITEBITS)
#define gray2black(x) l_setbit((x)->marked, BLACKBIT) #define nw2black(x) \
check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT))
#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS) #define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS)

View File

@ -52,6 +52,12 @@ static int l_checkmode (const char *mode) {
** ======================================================= ** =======================================================
*/ */
#if !defined(l_checkmodep)
/* By default, Lua accepts only "r" or "w" as mode */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
#endif
#if !defined(l_popen) /* { */ #if !defined(l_popen) /* { */
#if defined(LUA_USE_POSIX) /* { */ #if defined(LUA_USE_POSIX) /* { */
@ -279,6 +285,7 @@ static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r"); const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L); LStream *p = newprefile(L);
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
p->f = l_popen(L, filename, mode); p->f = l_popen(L, filename, mode);
p->closef = &io_pclose; p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;

View File

@ -81,7 +81,6 @@ void luaX_init (lua_State *L) {
const char *luaX_token2str (LexState *ls, int token) { const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) { /* single-byte symbols? */ if (token < FIRST_RESERVED) { /* single-byte symbols? */
lua_assert(token == cast_uchar(token));
if (lisprint(token)) if (lisprint(token))
return luaO_pushfstring(ls->L, "'%c'", token); return luaO_pushfstring(ls->L, "'%c'", token);
else /* control character */ else /* control character */
@ -255,9 +254,10 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
/* /*
** reads a sequence '[=*[' or ']=*]', leaving the last bracket. ** read a sequence '[=*[' or ']=*]', leaving the last bracket. If
** If sequence is well formed, return its number of '='s + 2; otherwise, ** sequence is well formed, return its number of '='s + 2; otherwise,
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...'). ** return 1 if it is a single bracket (no '='s and no 2nd bracket);
** otherwise (an unfinished '[==...') return 0.
*/ */
static size_t skip_sep (LexState *ls) { static size_t skip_sep (LexState *ls) {
size_t count = 0; size_t count = 0;
@ -482,34 +482,34 @@ static int llex (LexState *ls, SemInfo *seminfo) {
} }
case '=': { case '=': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_EQ; if (check_next1(ls, '=')) return TK_EQ; /* '==' */
else return '='; else return '=';
} }
case '<': { case '<': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_LE; if (check_next1(ls, '=')) return TK_LE; /* '<=' */
else if (check_next1(ls, '<')) return TK_SHL; else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */
else return '<'; else return '<';
} }
case '>': { case '>': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_GE; if (check_next1(ls, '=')) return TK_GE; /* '>=' */
else if (check_next1(ls, '>')) return TK_SHR; else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */
else return '>'; else return '>';
} }
case '/': { case '/': {
next(ls); next(ls);
if (check_next1(ls, '/')) return TK_IDIV; if (check_next1(ls, '/')) return TK_IDIV; /* '//' */
else return '/'; else return '/';
} }
case '~': { case '~': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_NE; if (check_next1(ls, '=')) return TK_NE; /* '~=' */
else return '~'; else return '~';
} }
case ':': { case ':': {
next(ls); next(ls);
if (check_next1(ls, ':')) return TK_DBCOLON; if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */
else return ':'; else return ':';
} }
case '"': case '\'': { /* short literal strings */ case '"': case '\'': { /* short literal strings */
@ -548,7 +548,7 @@ static int llex (LexState *ls, SemInfo *seminfo) {
return TK_NAME; return TK_NAME;
} }
} }
else { /* single-char tokens (+ - / ...) */ else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */
int c = ls->current; int c = ls->current;
next(ls); next(ls);
return c; return c;

View File

@ -7,11 +7,17 @@
#ifndef llex_h #ifndef llex_h
#define llex_h #define llex_h
#include <limits.h>
#include "lobject.h" #include "lobject.h"
#include "lzio.h" #include "lzio.h"
#define FIRST_RESERVED 257 /*
** Single-char tokens (terminal symbols) are represented by their own
** numeric code. Other tokens start at the following value.
*/
#define FIRST_RESERVED (UCHAR_MAX + 1)
#if !defined(LUA_ENV) #if !defined(LUA_ENV)

View File

@ -84,7 +84,15 @@ typedef LUAI_UACNUMBER l_uacNumber;
typedef LUAI_UACINT l_uacInt; typedef LUAI_UACINT l_uacInt;
/* internal assertions for in-house debugging */ /*
** Internal assertions for in-house debugging
*/
#if defined LUAI_ASSERT
#undef NDEBUG
#include <assert.h>
#define lua_assert(c) assert(c)
#endif
#if defined(lua_assert) #if defined(lua_assert)
#define check_exp(c,e) (lua_assert(c), (e)) #define check_exp(c,e) (lua_assert(c), (e))
/* to avoid problems with conditions too long */ /* to avoid problems with conditions too long */
@ -226,6 +234,17 @@ typedef l_uint32 Instruction;
#endif #endif
/*
** Maximum depth for nested C calls, syntactical nested non-terminals,
** and other features implemented through recursion in C. (Value must
** fit in a 16-bit unsigned integer. It must also be compatible with
** the size of the C stack.)
*/
#if !defined(LUAI_MAXCCALLS)
#define LUAI_MAXCCALLS 200
#endif
/* /*
** macros that are executed whenever program enters the Lua core ** macros that are executed whenever program enters the Lua core
** ('lua_lock') and leaves the core ('lua_unlock') ** ('lua_lock') and leaves the core ('lua_unlock')
@ -307,7 +326,8 @@ typedef l_uint32 Instruction;
/* exponentiation */ /* exponentiation */
#if !defined(luai_numpow) #if !defined(luai_numpow)
#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b)) #define luai_numpow(L,a,b) \
((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b))
#endif #endif
/* the others are quite standard operations */ /* the others are quite standard operations */
@ -336,7 +356,7 @@ typedef l_uint32 Instruction;
#else #else
/* realloc stack keeping its size */ /* realloc stack keeping its size */
#define condmovestack(L,pre,pos) \ #define condmovestack(L,pre,pos) \
{ int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_, 0); pos; } { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
#endif #endif
#if !defined(HARDMEMTESTS) #if !defined(HARDMEMTESTS)

View File

@ -22,7 +22,7 @@
#include "lstate.h" #include "lstate.h"
#if defined(HARDMEMTESTS) #if defined(EMERGENCYGCTESTS)
/* /*
** First allocation will fail whenever not building initial state ** First allocation will fail whenever not building initial state
** and not shrinking a block. (This fail will trigger 'tryagain' and ** and not shrinking a block. (This fail will trigger 'tryagain' and

View File

@ -215,37 +215,42 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
/* }====================================================== */ /* }====================================================== */
/* maximum length of a numeral */ /* maximum length of a numeral to be converted to a number */
#if !defined (L_MAXLENNUM) #if !defined (L_MAXLENNUM)
#define L_MAXLENNUM 200 #define L_MAXLENNUM 200
#endif #endif
/*
** Convert string 's' to a Lua number (put in 'result'). Return NULL on
** fail or the address of the ending '\0' on success. ('mode' == 'x')
** means a hexadecimal numeral.
*/
static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
char *endptr; char *endptr;
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
: lua_str2number(s, &endptr); : lua_str2number(s, &endptr);
if (endptr == s) return NULL; /* nothing recognized? */ if (endptr == s) return NULL; /* nothing recognized? */
while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */
return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */ return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */
} }
/* /*
** Convert string 's' to a Lua number (put in 'result'). Return NULL ** Convert string 's' to a Lua number (put in 'result') handling the
** on fail or the address of the ending '\0' on success. ** current locale.
** 'pmode' points to (and 'mode' contains) special things in the string:
** - 'x'/'X' means a hexadecimal numeral
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
** - '.' just optimizes the search for the common case (nothing special)
** This function accepts both the current locale or a dot as the radix ** This function accepts both the current locale or a dot as the radix
** mark. If the conversion fails, it may mean number has a dot but ** mark. If the conversion fails, it may mean number has a dot but
** locale accepts something else. In that case, the code copies 's' ** locale accepts something else. In that case, the code copies 's'
** to a buffer (because 's' is read-only), changes the dot to the ** to a buffer (because 's' is read-only), changes the dot to the
** current locale radix mark, and tries to convert again. ** current locale radix mark, and tries to convert again.
** The variable 'mode' checks for special characters in the string:
** - 'n' means 'inf' or 'nan' (which should be rejected)
** - 'x' means a hexadecimal numeral
** - '.' just optimizes the search for the common case (no special chars)
*/ */
static const char *l_str2d (const char *s, lua_Number *result) { static const char *l_str2d (const char *s, lua_Number *result) {
const char *endptr; const char *endptr;
const char *pmode = strpbrk(s, ".xXnN"); const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */
int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
if (mode == 'n') /* reject 'inf' and 'nan' */ if (mode == 'n') /* reject 'inf' and 'nan' */
return NULL; return NULL;
@ -253,7 +258,7 @@ static const char *l_str2d (const char *s, lua_Number *result) {
if (endptr == NULL) { /* failed? may be a different locale */ if (endptr == NULL) { /* failed? may be a different locale */
char buff[L_MAXLENNUM + 1]; char buff[L_MAXLENNUM + 1];
const char *pdot = strchr(s, '.'); const char *pdot = strchr(s, '.');
if (strlen(s) > L_MAXLENNUM || pdot == NULL) if (pdot == NULL || strlen(s) > L_MAXLENNUM)
return NULL; /* string too long or no dot; fail */ return NULL; /* string too long or no dot; fail */
strcpy(buff, s); /* copy string to buffer */ strcpy(buff, s); /* copy string to buffer */
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
@ -333,8 +338,15 @@ int luaO_utf8esc (char *buff, unsigned long x) {
} }
/* maximum length of the conversion of a number to a string */ /*
#define MAXNUMBER2STR 50 ** Maximum length of the conversion of a number to a string. Must be
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
** (For a long long int, this is 19 digits plus a sign and a final '\0',
** adding to 21. For a long double, it can go to a sign, 33 digits,
** the dot, an exponent letter, an exponent sign, 5 exponent digits,
** and a final '\0', adding to 43.)
*/
#define MAXNUMBER2STR 44
/* /*
@ -375,7 +387,7 @@ void luaO_tostring (lua_State *L, TValue *obj) {
*/ */
/* size for buffer space used by 'luaO_pushvfstring' */ /* size for buffer space used by 'luaO_pushvfstring' */
#define BUFVFS 400 #define BUFVFS 200
/* buffer used by 'luaO_pushvfstring' */ /* buffer used by 'luaO_pushvfstring' */
typedef struct BuffFS { typedef struct BuffFS {
@ -387,19 +399,17 @@ typedef struct BuffFS {
/* /*
** Push given string to the stack, as part of the buffer. If the stack ** Push given string to the stack, as part of the buffer, and
** is almost full, join all partial strings in the stack into one. ** join the partial strings in the stack into one.
*/ */
static void pushstr (BuffFS *buff, const char *str, size_t l) { static void pushstr (BuffFS *buff, const char *str, size_t l) {
lua_State *L = buff->L; lua_State *L = buff->L;
setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
L->top++; /* may use one extra slot */ L->top++; /* may use one extra slot */
buff->pushed++; buff->pushed++;
if (buff->pushed > 1 && L->top + 1 >= L->stack_last) { luaV_concat(L, buff->pushed); /* join partial results into one */
luaV_concat(L, buff->pushed); /* join all partial results into one */
buff->pushed = 1; buff->pushed = 1;
} }
}
/* /*
@ -521,8 +531,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
} }
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
clearbuff(&buff); /* empty buffer into the stack */ clearbuff(&buff); /* empty buffer into the stack */
if (buff.pushed > 1) lua_assert(buff.pushed == 1);
luaV_concat(L, buff.pushed); /* join all partial results */
return svalue(s2v(L->top - 1)); return svalue(s2v(L->top - 1));
} }

View File

@ -21,10 +21,12 @@
*/ */
#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ #define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ #define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */
/* /*
** number of all possible types (including LUA_TNONE) ** number of all possible types (including LUA_TNONE but excluding DEADKEY)
*/ */
#define LUA_TOTALTYPES (LUA_TPROTO + 2) #define LUA_TOTALTYPES (LUA_TPROTO + 2)
@ -96,7 +98,8 @@ typedef struct TValue {
/* /*
** Any value being manipulated by the program either is non ** Any value being manipulated by the program either is non
** collectable, or the collectable object has the right tag ** collectable, or the collectable object has the right tag
** and it is not dead. ** and it is not dead. The option 'L == NULL' allows other
** macros using this one to be used where L is not available.
*/ */
#define checkliveness(L,obj) \ #define checkliveness(L,obj) \
((void)L, lua_longassert(!iscollectable(obj) || \ ((void)L, lua_longassert(!iscollectable(obj) || \
@ -554,7 +557,7 @@ typedef struct Proto {
/* /*
** {================================================================== ** {==================================================================
** Closures ** Functions
** =================================================================== ** ===================================================================
*/ */
@ -703,9 +706,9 @@ typedef union Node {
*/ */
#define BITRAS (1 << 7) #define BITRAS (1 << 7)
#define isrealasize(t) (!((t)->marked & BITRAS)) #define isrealasize(t) (!((t)->flags & BITRAS))
#define setrealasize(t) ((t)->marked &= cast_byte(~BITRAS)) #define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
#define setnorealasize(t) ((t)->marked |= BITRAS) #define setnorealasize(t) ((t)->flags |= BITRAS)
typedef struct Table { typedef struct Table {
@ -742,13 +745,13 @@ typedef struct Table {
/* /*
** Use a "nil table" to mark dead keys in a table. Those keys serve ** Dead keys in tables have the tag DEADKEY but keep their original
** to keep space for removed entries, which may still be part of ** gcvalue. This distinguishes them from regular keys but allows them to
** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE ** be found when searched in a special way. ('next' needs that to find
** set, so these values are considered not collectable and are different ** keys removed from a table during a traversal.)
** from any valid value.
*/ */
#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL) #define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
/* }================================================================== */ /* }================================================================== */

View File

@ -261,7 +261,7 @@ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
OP_UNM,/* A B R[A] := -R[B] */ OP_UNM,/* A B R[A] := -R[B] */
OP_BNOT,/* A B R[A] := ~R[B] */ OP_BNOT,/* A B R[A] := ~R[B] */
OP_NOT,/* A B R[A] := not R[B] */ OP_NOT,/* A B R[A] := not R[B] */
OP_LEN,/* A B R[A] := length of R[B] */ OP_LEN,/* A B R[A] := #R[B] (length operator) */
OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
@ -297,7 +297,7 @@ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
OP_SETLIST,/* A B C k R[A][(C-1)*FPF+i] := R[A+i], 1 <= i <= B */ OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */

View File

@ -489,12 +489,10 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
} }
/* #define enterlevel(ls) luaE_incCstack(ls->L)
** Macros to limit the maximum recursion depth while parsing
*/
#define enterlevel(ls) luaE_enterCcall((ls)->L)
#define leavelevel(ls) luaE_exitCcall((ls)->L)
#define leavelevel(ls) ((ls)->L->nCcalls--)
/* /*
@ -947,7 +945,7 @@ static void setvararg (FuncState *fs, int nparams) {
static void parlist (LexState *ls) { static void parlist (LexState *ls) {
/* parlist -> [ param { ',' param } ] */ /* parlist -> [ {NAME ','} (NAME | '...') ] */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Proto *f = fs->f; Proto *f = fs->f;
int nparams = 0; int nparams = 0;
@ -955,12 +953,12 @@ static void parlist (LexState *ls) {
if (ls->t.token != ')') { /* is 'parlist' not empty? */ if (ls->t.token != ')') { /* is 'parlist' not empty? */
do { do {
switch (ls->t.token) { switch (ls->t.token) {
case TK_NAME: { /* param -> NAME */ case TK_NAME: {
new_localvar(ls, str_checkname(ls)); new_localvar(ls, str_checkname(ls));
nparams++; nparams++;
break; break;
} }
case TK_DOTS: { /* param -> '...' */ case TK_DOTS: {
luaX_next(ls); luaX_next(ls);
isvararg = 1; isvararg = 1;
break; break;
@ -1625,59 +1623,21 @@ static void forstat (LexState *ls, int line) {
} }
/*
** Check whether next instruction is a single jump (a 'break', a 'goto'
** to a forward label, or a 'goto' to a backward label with no variable
** to close). If so, set the name of the 'label' it is jumping to
** ("break" for a 'break') or to where it is jumping to ('target') and
** return true. If not a single jump, leave input unchanged, to be
** handled as a regular statement.
*/
static int issinglejump (LexState *ls, TString **label, int *target) {
if (testnext(ls, TK_BREAK)) { /* a break? */
*label = luaS_newliteral(ls->L, "break");
return 1;
}
else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME)
return 0; /* not a valid goto */
else {
TString *lname = ls->lookahead.seminfo.ts; /* label's id */
Labeldesc *lb = findlabel(ls, lname);
if (lb) { /* a backward jump? */
/* does it need to close variables? */
if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar))
return 0; /* not a single jump; cannot optimize */
*target = lb->pc;
}
else /* jump forward */
*label = lname;
luaX_next(ls); /* skip goto */
luaX_next(ls); /* skip name */
return 1;
}
}
static void test_then_block (LexState *ls, int *escapelist) { static void test_then_block (LexState *ls, int *escapelist) {
/* test_then_block -> [IF | ELSEIF] cond THEN block */ /* test_then_block -> [IF | ELSEIF] cond THEN block */
BlockCnt bl; BlockCnt bl;
int line;
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
TString *jlb = NULL;
int target = NO_JUMP;
expdesc v; expdesc v;
int jf; /* instruction to skip 'then' code (if condition is false) */ int jf; /* instruction to skip 'then' code (if condition is false) */
luaX_next(ls); /* skip IF or ELSEIF */ luaX_next(ls); /* skip IF or ELSEIF */
expr(ls, &v); /* read condition */ expr(ls, &v); /* read condition */
checknext(ls, TK_THEN); checknext(ls, TK_THEN);
line = ls->linenumber; if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */
if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */ int line = ls->linenumber;
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */
luaX_next(ls); /* skip 'break' */
enterblock(fs, &bl, 0); /* must enter block before 'goto' */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */
if (jlb != NULL) /* forward jump? */ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
newgotoentry(ls, jlb, line, v.t); /* will be resolved later */
else /* backward jump */
luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */
while (testnext(ls, ';')) {} /* skip semicolons */ while (testnext(ls, ';')) {} /* skip semicolons */
if (block_follow(ls, 0)) { /* jump is the entire block? */ if (block_follow(ls, 0)) { /* jump is the entire block? */
leaveblock(fs); leaveblock(fs);
@ -1686,7 +1646,7 @@ static void test_then_block (LexState *ls, int *escapelist) {
else /* must skip over 'then' part if condition is false */ else /* must skip over 'then' part if condition is false */
jf = luaK_jump(fs); jf = luaK_jump(fs);
} }
else { /* regular case (not a jump) */ else { /* regular case (not a break) */
luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
enterblock(fs, &bl, 0); enterblock(fs, &bl, 0);
jf = v.f; jf = v.f;
@ -1754,7 +1714,7 @@ static void checktoclose (LexState *ls, int level) {
static void localstat (LexState *ls) { static void localstat (LexState *ls) {
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */ /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int toclose = -1; /* index of to-be-closed variable (if any) */ int toclose = -1; /* index of to-be-closed variable (if any) */
Vardesc *var; /* last variable */ Vardesc *var; /* last variable */

View File

@ -23,7 +23,7 @@
/* kinds of variables/expressions */ /* kinds of variables/expressions */
typedef enum { typedef enum {
VVOID, /* when 'expdesc' describes the last expression a list, VVOID, /* when 'expdesc' describes the last expression of a list,
this kind means an empty list (so, no expression) */ this kind means an empty list (so, no expression) */
VNIL, /* constant nil */ VNIL, /* constant nil */
VTRUE, /* constant true */ VTRUE, /* constant true */
@ -38,7 +38,8 @@ typedef enum {
VLOCAL, /* local variable; var.sidx = stack index (local register); VLOCAL, /* local variable; var.sidx = stack index (local register);
var.vidx = relative index in 'actvar.arr' */ var.vidx = relative index in 'actvar.arr' */
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */ VCONST, /* compile-time <const> variable;
info = absolute index in 'actvar.arr' */
VINDEXED, /* indexed variable; VINDEXED, /* indexed variable;
ind.t = table register; ind.t = table register;
ind.idx = key's R index */ ind.idx = key's R index */

View File

@ -76,7 +76,7 @@ static unsigned int luai_makeseed (lua_State *L) {
addbuff(buff, p, &h); /* local variable */ addbuff(buff, p, &h); /* local variable */
addbuff(buff, p, &lua_newstate); /* public function */ addbuff(buff, p, &lua_newstate); /* public function */
lua_assert(p == sizeof(buff)); lua_assert(p == sizeof(buff));
return luaS_hash(buff, p, h, 1); return luaS_hash(buff, p, h);
} }
#endif #endif
@ -97,66 +97,14 @@ void luaE_setdebt (global_State *g, l_mem debt) {
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
global_State *g = G(L); UNUSED(L); UNUSED(limit);
int ccalls; return LUAI_MAXCCALLS; /* warning?? */
luaE_freeCI(L); /* release unused CIs */
ccalls = getCcalls(L);
if (limit >= 40000)
return 0; /* out of bounds */
limit += CSTACKERR;
if (L != g-> mainthread)
return 0; /* only main thread can change the C stack */
else if (ccalls <= CSTACKERR)
return 0; /* handling overflow */
else {
int diff = limit - g->Cstacklimit;
if (ccalls + diff <= CSTACKERR)
return 0; /* new limit would cause an overflow */
g->Cstacklimit = limit; /* set new limit */
L->nCcalls += diff; /* correct 'nCcalls' */
return limit - diff - CSTACKERR; /* success; return previous limit */
}
}
/*
** Decrement count of "C calls" and check for overflows. In case of
** a stack overflow, check appropriate error ("regular" overflow or
** overflow while handling stack overflow). If 'nCcalls' is smaller
** than CSTACKERR but larger than CSTACKMARK, it means it has just
** entered the "overflow zone", so the function raises an overflow
** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is
** already handling an overflow) but larger than CSTACKERRMARK, does
** not report an error (to allow message handling to work). Otherwise,
** report a stack overflow while handling a stack overflow (probably
** caused by a repeating error in the message handling function).
*/
void luaE_enterCcall (lua_State *L) {
int ncalls = getCcalls(L);
L->nCcalls--;
if (ncalls <= CSTACKERR) { /* possible overflow? */
luaE_freeCI(L); /* release unused CIs */
ncalls = getCcalls(L); /* update call count */
if (ncalls <= CSTACKERR) { /* still overflow? */
if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
else if (ncalls >= CSTACKMARK) {
/* not in error-handling zone; raise the error now */
L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */
luaG_runerror(L, "C stack overflow");
}
/* else stack is in the error-handling zone;
allow message handler to work */
}
}
} }
CallInfo *luaE_extendCI (lua_State *L) { CallInfo *luaE_extendCI (lua_State *L) {
CallInfo *ci; CallInfo *ci;
lua_assert(L->ci->next == NULL); lua_assert(L->ci->next == NULL);
luaE_enterCcall(L);
ci = luaM_new(L, CallInfo); ci = luaM_new(L, CallInfo);
lua_assert(L->ci->next == NULL); lua_assert(L->ci->next == NULL);
L->ci->next = ci; L->ci->next = ci;
@ -175,13 +123,11 @@ void luaE_freeCI (lua_State *L) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
CallInfo *next = ci->next; CallInfo *next = ci->next;
ci->next = NULL; ci->next = NULL;
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
while ((ci = next) != NULL) { while ((ci = next) != NULL) {
next = ci->next; next = ci->next;
luaM_free(L, ci); luaM_free(L, ci);
L->nci--; L->nci--;
} }
L->nCcalls -= L->nci; /* adjust result */
} }
@ -194,7 +140,6 @@ void luaE_shrinkCI (lua_State *L) {
CallInfo *next; CallInfo *next;
if (ci == NULL) if (ci == NULL)
return; /* no extra elements */ return; /* no extra elements */
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
while ((next = ci->next) != NULL) { /* two extra elements? */ while ((next = ci->next) != NULL) { /* two extra elements? */
CallInfo *next2 = next->next; /* next's next */ CallInfo *next2 = next->next; /* next's next */
ci->next = next2; /* remove next from the list */ ci->next = next2; /* remove next from the list */
@ -207,19 +152,39 @@ void luaE_shrinkCI (lua_State *L) {
ci = next2; /* continue */ ci = next2; /* continue */
} }
} }
L->nCcalls -= L->nci; /* adjust result */ }
/*
** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS.
** If equal, raises an overflow error. If value is larger than
** LUAI_MAXCCALLS (which means it is handling an overflow) but
** not much larger, does not report an error (to allow overflow
** handling to work).
*/
void luaE_checkcstack (lua_State *L) {
if (getCcalls(L) == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow");
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
}
LUAI_FUNC void luaE_incCstack (lua_State *L) {
L->nCcalls++;
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
} }
static void stack_init (lua_State *L1, lua_State *L) { static void stack_init (lua_State *L1, lua_State *L) {
int i; CallInfo *ci; int i; CallInfo *ci;
/* initialize stack array */ /* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, StackValue); L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->stacksize = BASIC_STACK_SIZE; for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
for (i = 0; i < BASIC_STACK_SIZE; i++)
setnilvalue(s2v(L1->stack + i)); /* erase new stack */ setnilvalue(s2v(L1->stack + i)); /* erase new stack */
L1->top = L1->stack; L1->top = L1->stack;
L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK; L1->stack_last = L1->stack + BASIC_STACK_SIZE;
/* initialize first ci */ /* initialize first ci */
ci = &L1->base_ci; ci = &L1->base_ci;
ci->next = ci->previous = NULL; ci->next = ci->previous = NULL;
@ -240,7 +205,7 @@ static void freestack (lua_State *L) {
L->ci = &L->base_ci; /* free the entire 'ci' list */ L->ci = &L->base_ci; /* free the entire 'ci' list */
luaE_freeCI(L); luaE_freeCI(L);
lua_assert(L->nci == 0); lua_assert(L->nci == 0);
luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */
} }
@ -290,7 +255,6 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->stack = NULL; L->stack = NULL;
L->ci = NULL; L->ci = NULL;
L->nci = 0; L->nci = 0;
L->stacksize = 0;
L->twups = L; /* thread has no upvalues */ L->twups = L; /* thread has no upvalues */
L->errorJmp = NULL; L->errorJmp = NULL;
L->hook = NULL; L->hook = NULL;
@ -301,6 +265,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->openupval = NULL; L->openupval = NULL;
L->status = LUA_OK; L->status = LUA_OK;
L->errfunc = 0; L->errfunc = 0;
L->oldpc = 0;
} }
@ -318,9 +283,10 @@ static void close_state (lua_State *L) {
LUA_API lua_State *lua_newthread (lua_State *L) { LUA_API lua_State *lua_newthread (lua_State *L) {
global_State *g = G(L); global_State *g;
lua_State *L1; lua_State *L1;
lua_lock(L); lua_lock(L);
g = G(L);
luaC_checkGC(L); luaC_checkGC(L);
/* create new thread */ /* create new thread */
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
@ -333,7 +299,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
setthvalue2s(L, L->top, L1); setthvalue2s(L, L->top, L1);
api_incr_top(L); api_incr_top(L);
preinit_thread(L1, g); preinit_thread(L1, g);
L1->nCcalls = getCcalls(L); L1->nCcalls = 0;
L1->hookmask = L->hookmask; L1->hookmask = L->hookmask;
L1->basehookcount = L->basehookcount; L1->basehookcount = L->basehookcount;
L1->hook = L->hook; L1->hook = L->hook;
@ -394,7 +360,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
preinit_thread(L, g); preinit_thread(L, g);
g->allgc = obj2gco(L); /* by now, only object is the main thread */ g->allgc = obj2gco(L); /* by now, only object is the main thread */
L->next = NULL; L->next = NULL;
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR; L->nCcalls = 0;
incnny(L); /* main thread is always non yieldable */
g->frealloc = f; g->frealloc = f;
g->ud = ud; g->ud = ud;
g->warnf = NULL; g->warnf = NULL;
@ -410,8 +377,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->gckind = KGC_INC; g->gckind = KGC_INC;
g->gcemergency = 0; g->gcemergency = 0;
g->finobj = g->tobefnz = g->fixedgc = NULL; g->finobj = g->tobefnz = g->fixedgc = NULL;
g->survival = g->old = g->reallyold = NULL; g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
g->finobjsur = g->finobjold = g->finobjrold = NULL; g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
g->sweepgc = NULL; g->sweepgc = NULL;
g->gray = g->grayagain = NULL; g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL; g->weak = g->ephemeron = g->allweak = NULL;
@ -436,8 +403,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
LUA_API void lua_close (lua_State *L) { LUA_API void lua_close (lua_State *L) {
L = G(L)->mainthread; /* only the main thread can be closed */
lua_lock(L); lua_lock(L);
L = G(L)->mainthread; /* only the main thread can be closed */
close_state(L); close_state(L);
} }

View File

@ -32,13 +32,29 @@
** **
** 'allgc' -> 'survival': new objects; ** 'allgc' -> 'survival': new objects;
** 'survival' -> 'old': objects that survived one collection; ** 'survival' -> 'old': objects that survived one collection;
** 'old' -> 'reallyold': objects that became old in last collection; ** 'old1' -> 'reallyold': objects that became old in last collection;
** 'reallyold' -> NULL: objects old for more than one cycle. ** 'reallyold' -> NULL: objects old for more than one cycle.
** **
** 'finobj' -> 'finobjsur': new objects marked for finalization; ** 'finobj' -> 'finobjsur': new objects marked for finalization;
** 'finobjsur' -> 'finobjold': survived """"; ** 'finobjsur' -> 'finobjold1': survived """";
** 'finobjold' -> 'finobjrold': just old """"; ** 'finobjold1' -> 'finobjrold': just old """";
** 'finobjrold' -> NULL: really old """". ** 'finobjrold' -> NULL: really old """".
**
** All lists can contain elements older than their main ages, due
** to 'luaC_checkfinalizer' and 'udata2finalize', which move
** objects between the normal lists and the "marked for finalization"
** lists. Moreover, barriers can age young objects in young lists as
** OLD0, which then become OLD1. However, a list never contains
** elements younger than their main ages.
**
** The generational collector also uses a pointer 'firstold1', which
** points to the first OLD1 object in the list. It is used to optimize
** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
** and 'reallyold', but often the list has no OLD1 objects or they are
** after 'old1'.) Note the difference between it and 'old1':
** 'firstold1': no OLD1 objects before this point; there can be all
** ages after it.
** 'old1': no objects younger than OLD1 after this point.
*/ */
/* /*
@ -47,7 +63,7 @@
** can become gray have such a field. The field is not the same ** can become gray have such a field. The field is not the same
** in all objects, but it always has this name.) Any gray object ** in all objects, but it always has this name.) Any gray object
** must belong to one of these lists, and all objects in these lists ** must belong to one of these lists, and all objects in these lists
** must be gray: ** must be gray (with two exceptions explained below):
** **
** 'gray': regular gray objects, still waiting to be visited. ** 'gray': regular gray objects, still waiting to be visited.
** 'grayagain': objects that must be revisited at the atomic phase. ** 'grayagain': objects that must be revisited at the atomic phase.
@ -58,54 +74,26 @@
** 'weak': tables with weak values to be cleared; ** 'weak': tables with weak values to be cleared;
** 'ephemeron': ephemeron tables with white->white entries; ** 'ephemeron': ephemeron tables with white->white entries;
** 'allweak': tables with weak keys and/or weak values to be cleared. ** 'allweak': tables with weak keys and/or weak values to be cleared.
**
** The exceptions to that "gray rule" are:
** - TOUCHED2 objects in generational mode stay in a gray list (because
** they must be visited again at the end of the cycle), but they are
** marked black because assignments to them must activate barriers (to
** move them back to TOUCHED1).
** - Open upvales are kept gray to avoid barriers, but they stay out
** of gray lists. (They don't even have a 'gclist' field.)
*/ */
/* /*
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of ** About 'nCcalls': This count has two parts: the lower 16 bits counts
** how many "C calls" it still can do in the C stack, to avoid C-stack ** the number of recursive invocations in the C stack; the higher
** overflow. This count is very rough approximation; it considers only ** 16 bits counts the number of non-yieldable calls in the stack.
** recursive functions inside the interpreter, as non-recursive calls ** (They are together so that we can change and save both with one
** can be considered using a fixed (although unknown) amount of stack ** instruction.)
** space.
**
** The count has two parts: the lower part is the count itself; the
** higher part counts the number of non-yieldable calls in the stack.
** (They are together so that we can change both with one instruction.)
**
** Because calls to external C functions can use an unknown amount
** of space (e.g., functions using an auxiliary buffer), calls
** to these functions add more than one to the count (see CSTACKCF).
**
** The proper count excludes the number of CallInfo structures allocated
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
** (and "consumes" one CallInfo), it needs neither to decrement nor to
** check 'nCcalls', as its use of C stack is already accounted for.
*/ */
/* number of "C stack slots" used by an external C function */
#define CSTACKCF 10
/*
** The C-stack size is sliced in the following zones:
** - larger than CSTACKERR: normal stack;
** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow;
** - [CSTACKCF, CSTACKERRMARK]: error-handling zone;
** - below CSTACKERRMARK: buffer zone to signal overflow during overflow;
** (Because the counter can be decremented CSTACKCF at once, we need
** the so called "buffer zones", with at least that size, to properly
** detect a change from one zone to the next.)
*/
#define CSTACKERR (8 * CSTACKCF)
#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2))
#define CSTACKERRMARK (CSTACKCF + 2)
/* initial limit for the C-stack of threads */
#define CSTACKTHREAD (2 * CSTACKERR)
/* true if this thread does not have non-yieldable calls in the stack */ /* true if this thread does not have non-yieldable calls in the stack */
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
@ -120,13 +108,8 @@
/* Decrement the number of non-yieldable calls */ /* Decrement the number of non-yieldable calls */
#define decnny(L) ((L)->nCcalls -= 0x10000) #define decnny(L) ((L)->nCcalls -= 0x10000)
/* Increment the number of non-yieldable calls and decrement nCcalls */ /* Non-yieldable call increment */
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF) #define nyci (0x10000 | 1)
/* Decrement the number of non-yieldable calls and increment nCcalls */
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
@ -144,12 +127,20 @@ struct lua_longjmp; /* defined in ldo.c */
#endif #endif
/* extra stack space to handle TM calls and some other extras */ /*
** Extra stack space to handle TM calls and some other extras. This
** space is not included in 'stack_last'. It is used only to avoid stack
** checks, either because the element will be promptly popped or because
** there will be a stack check soon after the push. Function frames
** never use this extra space, so it does not need to be kept clean.
*/
#define EXTRA_STACK 5 #define EXTRA_STACK 5
#define BASIC_STACK_SIZE (2*LUA_MINSTACK) #define BASIC_STACK_SIZE (2*LUA_MINSTACK)
#define stacksize(th) cast_int((th)->stack_last - (th)->stack)
/* kinds of Garbage Collection */ /* kinds of Garbage Collection */
#define KGC_INC 0 /* incremental gc */ #define KGC_INC 0 /* incremental gc */
@ -200,14 +191,15 @@ typedef struct CallInfo {
*/ */
#define CIST_OAH (1<<0) /* original value of 'allowhook' */ #define CIST_OAH (1<<0) /* original value of 'allowhook' */
#define CIST_C (1<<1) /* call is running a C function */ #define CIST_C (1<<1) /* call is running a C function */
#define CIST_HOOKED (1<<2) /* call is running a debug hook */ #define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */ #define CIST_HOOKED (1<<3) /* call is running a debug hook */
#define CIST_TAIL (1<<4) /* call was tail called */ #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */ #define CIST_TAIL (1<<5) /* call was tail called */
#define CIST_FIN (1<<6) /* call is running a finalizer */ #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */ #define CIST_FIN (1<<7) /* call is running a finalizer */
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
#if defined(LUA_COMPAT_LT_LE) #if defined(LUA_COMPAT_LT_LE)
#define CIST_LEQ (1<<8) /* using __lt for __le */ #define CIST_LEQ (1<<9) /* using __lt for __le */
#endif #endif
/* active function is a Lua function */ /* active function is a Lua function */
@ -257,10 +249,11 @@ typedef struct global_State {
GCObject *fixedgc; /* list of objects not to be collected */ GCObject *fixedgc; /* list of objects not to be collected */
/* fields for generational collector */ /* fields for generational collector */
GCObject *survival; /* start of objects that survived one GC cycle */ GCObject *survival; /* start of objects that survived one GC cycle */
GCObject *old; /* start of old objects */ GCObject *old1; /* start of old1 objects */
GCObject *reallyold; /* old objects with more than one cycle */ GCObject *reallyold; /* objects more than one cycle old ("really old") */
GCObject *firstold1; /* first OLD1 object in the list (if any) */
GCObject *finobjsur; /* list of survival objects with finalizers */ GCObject *finobjsur; /* list of survival objects with finalizers */
GCObject *finobjold; /* list of old objects with finalizers */ GCObject *finobjold1; /* list of old1 objects with finalizers */
GCObject *finobjrold; /* list of really old objects with finalizers */ GCObject *finobjrold; /* list of really old objects with finalizers */
struct lua_State *twups; /* list of threads with open upvalues */ struct lua_State *twups; /* list of threads with open upvalues */
lua_CFunction panic; /* to be called in unprotected errors */ lua_CFunction panic; /* to be called in unprotected errors */
@ -271,7 +264,6 @@ typedef struct global_State {
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
lua_WarnFunction warnf; /* warning function */ lua_WarnFunction warnf; /* warning function */
void *ud_warn; /* auxiliary data to 'warnf' */ void *ud_warn; /* auxiliary data to 'warnf' */
unsigned int Cstacklimit; /* current limit for the C stack */
} global_State; } global_State;
@ -286,8 +278,7 @@ struct lua_State {
StkId top; /* first free slot in the stack */ StkId top; /* first free slot in the stack */
global_State *l_G; global_State *l_G;
CallInfo *ci; /* call info for current function */ CallInfo *ci; /* call info for current function */
const Instruction *oldpc; /* last pc traced */ StkId stack_last; /* end of stack (last element + 1) */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */ StkId stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */ UpVal *openupval; /* list of open upvalues in this stack */
GCObject *gclist; GCObject *gclist;
@ -296,8 +287,8 @@ struct lua_State {
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
volatile lua_Hook hook; volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */ ptrdiff_t errfunc; /* current error handling function (stack index) */
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */ l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
int stacksize; int oldpc; /* last pc traced */
int basehookcount; int basehookcount;
int hookcount; int hookcount;
volatile l_signalT hookmask; volatile l_signalT hookmask;
@ -309,6 +300,12 @@ struct lua_State {
/* /*
** Union of all collectable objects (only for conversions) ** Union of all collectable objects (only for conversions)
** ISO C99, 6.5.2.3 p.5:
** "if a union contains several structures that share a common initial
** sequence [...], and if the union object currently contains one
** of these structures, it is permitted to inspect the common initial
** part of any of them anywhere that a declaration of the complete type
** of the union is visible."
*/ */
union GCUnion { union GCUnion {
GCObject gc; /* common header */ GCObject gc; /* common header */
@ -322,6 +319,11 @@ union GCUnion {
}; };
/*
** ISO C99, 6.7.2.1 p.14:
** "A pointer to a union object, suitably converted, points to each of
** its members [...], and vice versa."
*/
#define cast_u(o) cast(union GCUnion *, (o)) #define cast_u(o) cast(union GCUnion *, (o))
/* macros to convert a GCObject into a specific value */ /* macros to convert a GCObject into a specific value */
@ -353,12 +355,11 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_enterCcall (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L);
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
#define luaE_exitCcall(L) ((L)->nCcalls++)
#endif #endif

View File

@ -22,16 +22,6 @@
#include "lstring.h" #include "lstring.h"
/*
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a long string to
** compute its hash
*/
#if !defined(LUAI_HASHLIMIT)
#define LUAI_HASHLIMIT 5
#endif
/* /*
** Maximum size for string table. ** Maximum size for string table.
*/ */
@ -50,10 +40,9 @@ int luaS_eqlngstr (TString *a, TString *b) {
} }
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed, unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
size_t step) {
unsigned int h = seed ^ cast_uint(l); unsigned int h = seed ^ cast_uint(l);
for (; l >= step; l -= step) for (; l > 0; l--)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
return h; return h;
} }
@ -63,8 +52,7 @@ unsigned int luaS_hashlongstr (TString *ts) {
lua_assert(ts->tt == LUA_VLNGSTR); lua_assert(ts->tt == LUA_VLNGSTR);
if (ts->extra == 0) { /* no hash? */ if (ts->extra == 0) { /* no hash? */
size_t len = ts->u.lnglen; size_t len = ts->u.lnglen;
size_t step = (len >> LUAI_HASHLIMIT) + 1; ts->hash = luaS_hash(getstr(ts), len, ts->hash);
ts->hash = luaS_hash(getstr(ts), len, ts->hash, step);
ts->extra = 1; /* now it has its hash */ ts->extra = 1; /* now it has its hash */
} }
return ts->hash; return ts->hash;
@ -201,7 +189,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
TString *ts; TString *ts;
global_State *g = G(L); global_State *g = G(L);
stringtable *tb = &g->strt; stringtable *tb = &g->strt;
unsigned int h = luaS_hash(str, l, g->seed, 1); unsigned int h = luaS_hash(str, l, g->seed);
TString **list = &tb->hash[lmod(h, tb->size)]; TString **list = &tb->hash[lmod(h, tb->size)];
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
for (ts = *list; ts != NULL; ts = ts->u.hnext) { for (ts = *list; ts != NULL; ts = ts->u.hnext) {

View File

@ -41,8 +41,7 @@
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) #define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
unsigned int seed, size_t step);
LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_resize (lua_State *L, int newsize);

View File

@ -1365,7 +1365,6 @@ typedef union Ftypes {
float f; float f;
double d; double d;
lua_Number n; lua_Number n;
char buff[5 * sizeof(lua_Number)]; /* enough for any float type */
} Ftypes; } Ftypes;
@ -1535,12 +1534,10 @@ static void packint (luaL_Buffer *b, lua_Unsigned n,
** Copy 'size' bytes from 'src' to 'dest', correcting endianness if ** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
** given 'islittle' is different from native endianness. ** given 'islittle' is different from native endianness.
*/ */
static void copywithendian (volatile char *dest, volatile const char *src, static void copywithendian (char *dest, const char *src,
int size, int islittle) { int size, int islittle) {
if (islittle == nativeendian.little) { if (islittle == nativeendian.little)
while (size-- != 0) memcpy(dest, src, size);
*(dest++) = *(src++);
}
else { else {
dest += size - 1; dest += size - 1;
while (size-- != 0) while (size-- != 0)
@ -1584,14 +1581,14 @@ static int str_pack (lua_State *L) {
break; break;
} }
case Kfloat: { /* floating-point options */ case Kfloat: { /* floating-point options */
volatile Ftypes u; Ftypes u;
char *buff = luaL_prepbuffsize(&b, size); char *buff = luaL_prepbuffsize(&b, size);
lua_Number n = luaL_checknumber(L, arg); /* get argument */ lua_Number n = luaL_checknumber(L, arg); /* get argument */
if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
else if (size == sizeof(u.d)) u.d = (double)n; else if (size == sizeof(u.d)) u.d = (double)n;
else u.n = n; else u.n = n;
/* move 'u' to final result, correcting endianness if needed */ /* move 'u' to final result, correcting endianness if needed */
copywithendian(buff, u.buff, size, h.islittle); copywithendian(buff, (char *)&u, size, h.islittle);
luaL_addsize(&b, size); luaL_addsize(&b, size);
break; break;
} }
@ -1717,9 +1714,9 @@ static int str_unpack (lua_State *L) {
break; break;
} }
case Kfloat: { case Kfloat: {
volatile Ftypes u; Ftypes u;
lua_Number num; lua_Number num;
copywithendian(u.buff, data + pos, size, h.islittle); copywithendian((char *)&u, data + pos, size, h.islittle);
if (size == sizeof(u.f)) num = (lua_Number)u.f; if (size == sizeof(u.f)) num = (lua_Number)u.f;
else if (size == sizeof(u.d)) num = (lua_Number)u.d; else if (size == sizeof(u.d)) num = (lua_Number)u.d;
else num = u.n; else num = u.n;
@ -1738,7 +1735,7 @@ static int str_unpack (lua_State *L) {
break; break;
} }
case Kzstr: { case Kzstr: {
size_t len = (int)strlen(data + pos); size_t len = strlen(data + pos);
luaL_argcheck(L, pos + len < ld, 2, luaL_argcheck(L, pos + len < ld, 2,
"unfinished string for format 'z'"); "unfinished string for format 'z'");
lua_pushlstring(L, data + pos, len); lua_pushlstring(L, data + pos, len);

View File

@ -166,17 +166,30 @@ static Node *mainpositionTV (const Table *t, const TValue *key) {
/* /*
** Check whether key 'k1' is equal to the key in node 'n2'. ** Check whether key 'k1' is equal to the key in node 'n2'. This
** This equality is raw, so there are no metamethods. Floats ** equality is raw, so there are no metamethods. Floats with integer
** with integer values have been normalized, so integers cannot ** values have been normalized, so integers cannot be equal to
** be equal to floats. It is assumed that 'eqshrstr' is simply ** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
** pointer equality, so that short strings are handled in the ** that short strings are handled in the default case.
** default case. ** A true 'deadok' means to accept dead keys as equal to their original
** values. All dead keys are compared in the default case, by pointer
** identity. (Only collectable objects can produce dead keys.) Note that
** dead long strings are also compared by identity.
** Once a key is dead, its corresponding value may be collected, and
** then another value can be created with the same address. If this
** other value is given to 'next', 'equalkey' will signal a false
** positive. In a regular traversal, this situation should never happen,
** as all keys given to 'next' came from the table itself, and therefore
** could not have been collected. Outside a regular traversal, we
** have garbage in, garbage out. What is relevant is that this false
** positive does not break anything. (In particular, 'next' will return
** some other valid item on the table or nil.)
*/ */
static int equalkey (const TValue *k1, const Node *n2) { static int equalkey (const TValue *k1, const Node *n2, int deadok) {
if (rawtt(k1) != keytt(n2)) /* not the same variants? */ if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
!(deadok && keyisdead(n2) && iscollectable(k1)))
return 0; /* cannot be same key */ return 0; /* cannot be same key */
switch (ttypetag(k1)) { switch (keytt(n2)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
return 1; return 1;
case LUA_VNUMINT: case LUA_VNUMINT:
@ -187,7 +200,7 @@ static int equalkey (const TValue *k1, const Node *n2) {
return pvalue(k1) == pvalueraw(keyval(n2)); return pvalue(k1) == pvalueraw(keyval(n2));
case LUA_VLCF: case LUA_VLCF:
return fvalue(k1) == fvalueraw(keyval(n2)); return fvalue(k1) == fvalueraw(keyval(n2));
case LUA_VLNGSTR: case ctb(LUA_VLNGSTR):
return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
default: default:
return gcvalue(k1) == gcvalueraw(keyval(n2)); return gcvalue(k1) == gcvalueraw(keyval(n2));
@ -251,11 +264,12 @@ static unsigned int setlimittosize (Table *t) {
/* /*
** "Generic" get version. (Not that generic: not valid for integers, ** "Generic" get version. (Not that generic: not valid for integers,
** which may be in array part, nor for floats with integral values.) ** which may be in array part, nor for floats with integral values.)
** See explanation about 'deadok' in function 'equalkey'.
*/ */
static const TValue *getgeneric (Table *t, const TValue *key) { static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
Node *n = mainpositionTV(t, key); Node *n = mainpositionTV(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */ for (;;) { /* check whether 'key' is somewhere in the chain */
if (equalkey(key, n)) if (equalkey(key, n, deadok))
return gval(n); /* that's it */ return gval(n); /* that's it */
else { else {
int nx = gnext(n); int nx = gnext(n);
@ -292,7 +306,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
if (i - 1u < asize) /* is 'key' inside array part? */ if (i - 1u < asize) /* is 'key' inside array part? */
return i; /* yes; that's the index */ return i; /* yes; that's the index */
else { else {
const TValue *n = getgeneric(t, key); const TValue *n = getgeneric(t, key, 1);
if (unlikely(isabstkey(n))) if (unlikely(isabstkey(n)))
luaG_runerror(L, "invalid key to 'next'"); /* key not found */ luaG_runerror(L, "invalid key to 'next'"); /* key not found */
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
@ -583,7 +597,7 @@ Table *luaH_new (lua_State *L) {
GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table));
Table *t = gco2t(o); Table *t = gco2t(o);
t->metatable = NULL; t->metatable = NULL;
t->flags = cast_byte(~0); t->flags = cast_byte(maskflags); /* table has no metamethod fields */
t->array = NULL; t->array = NULL;
t->alimit = 0; t->alimit = 0;
setnodevector(L, t, 0); setnodevector(L, t, 0);
@ -730,7 +744,7 @@ const TValue *luaH_getstr (Table *t, TString *key) {
else { /* for long strings, use generic case */ else { /* for long strings, use generic case */
TValue ko; TValue ko;
setsvalue(cast(lua_State *, NULL), &ko, key); setsvalue(cast(lua_State *, NULL), &ko, key);
return getgeneric(t, &ko); return getgeneric(t, &ko, 0);
} }
} }
@ -750,7 +764,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
/* else... */ /* else... */
} /* FALLTHROUGH */ } /* FALLTHROUGH */
default: default:
return getgeneric(t, key); return getgeneric(t, key, 0);
} }
} }

View File

@ -15,7 +15,12 @@
#define gnext(n) ((n)->u.next) #define gnext(n) ((n)->u.next)
#define invalidateTMcache(t) ((t)->flags = 0) /*
** Clear all bits of fast-access metamethods, which means that the table
** may have any of these metamethods. (First access that fails after the
** clearing will set the bit again.)
*/
#define invalidateTMcache(t) ((t)->flags &= ~maskflags)
/* true when 't' is using 'dummynode' as its hash part */ /* true when 't' is using 'dummynode' as its hash part */

View File

@ -240,7 +240,7 @@ void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */ int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
int nextra = actual - nfixparams; /* number of extra arguments */ int nextra = actual - nfixparams; /* number of extra arguments */
ci->u.l.nextraargs = nextra; ci->u.l.nextraargs = nextra;
checkstackGC(L, p->maxstacksize + 1); luaD_checkstack(L, p->maxstacksize + 1);
/* copy function to the top of the stack */ /* copy function to the top of the stack */
setobjs2s(L, L->top++, ci->func); setobjs2s(L, L->top++, ci->func);
/* move fixed parameters to the top of the stack */ /* move fixed parameters to the top of the stack */
@ -259,7 +259,7 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
int nextra = ci->u.l.nextraargs; int nextra = ci->u.l.nextraargs;
if (wanted < 0) { if (wanted < 0) {
wanted = nextra; /* get all extra arguments available */ wanted = nextra; /* get all extra arguments available */
checkstackp(L, nextra, where); /* ensure stack space */ checkstackGCp(L, nextra, where); /* ensure stack space */
L->top = where + nextra; /* next instruction will need top */ L->top = where + nextra; /* next instruction will need top */
} }
for (i = 0; i < wanted && i < nextra; i++) for (i = 0; i < wanted && i < nextra; i++)

View File

@ -45,6 +45,15 @@ typedef enum {
} TMS; } TMS;
/*
** Mask with 1 in all fast-access methods. A 1 in any of these bits
** in the flag of a (meta)table means the metatable does not have the
** corresponding metamethod field. (Bit 7 of the flag is used for
** 'isrealasize'.)
*/
#define maskflags (~(~0u << (TM_EQ + 1)))
/* /*
** Test whether there is no tagmethod. ** Test whether there is no tagmethod.
** (Because tagmethods use raw accesses, the result may be an "empty" nil.) ** (Because tagmethods use raw accesses, the result may be an "empty" nil.)

View File

@ -416,15 +416,19 @@ static int handle_luainit (lua_State *L) {
/* /*
** Returns the string to be used as a prompt by the interpreter. ** Return the string to be used as a prompt by the interpreter. Leave
** the string (or nil, if using the default value) on the stack, to keep
** it anchored.
*/ */
static const char *get_prompt (lua_State *L, int firstline) { static const char *get_prompt (lua_State *L, int firstline) {
const char *p; if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
p = lua_tostring(L, -1); else { /* apply 'tostring' over the value */
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); const char *p = luaL_tolstring(L, -1, NULL);
lua_remove(L, -2); /* remove original value */
return p; return p;
} }
}
/* mark in error messages for incomplete statements */ /* mark in error messages for incomplete statements */
#define EOFMARK "<eof>" #define EOFMARK "<eof>"

View File

@ -120,7 +120,10 @@ static TString *loadStringN (LoadState *S, Proto *p) {
} }
else { /* long string */ else { /* long string */
ts = luaS_createlngstrobj(L, size); /* create string */ ts = luaS_createlngstrobj(L, size); /* create string */
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
luaD_inctop(L);
loadVector(S, getstr(ts), size); /* load directly in final place */ loadVector(S, getstr(ts), size); /* load directly in final place */
L->top--; /* pop string */
} }
luaC_objbarrier(L, p, ts); luaC_objbarrier(L, p, ts);
return ts; return ts;
@ -200,13 +203,20 @@ static void loadProtos (LoadState *S, Proto *f) {
} }
/*
** Load the upvalues for a function. The names must be filled first,
** because the filling of the other fields can raise read errors and
** the creation of the error message can call an emergency collection;
** in that case all prototypes must be consistent for the GC.
*/
static void loadUpvalues (LoadState *S, Proto *f) { static void loadUpvalues (LoadState *S, Proto *f) {
int i, n; int i, n;
n = loadInt(S); n = loadInt(S);
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
f->sizeupvalues = n; f->sizeupvalues = n;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) /* make array valid for GC */
f->upvalues[i].name = NULL; f->upvalues[i].name = NULL;
for (i = 0; i < n; i++) { /* following calls can raise errors */
f->upvalues[i].instack = loadByte(S); f->upvalues[i].instack = loadByte(S);
f->upvalues[i].idx = loadByte(S); f->upvalues[i].idx = loadByte(S);
f->upvalues[i].kind = loadByte(S); f->upvalues[i].kind = loadByte(S);

View File

@ -229,7 +229,7 @@ static int forprep (lua_State *L, StkId ra) {
count /= l_castS2U(-(step + 1)) + 1u; count /= l_castS2U(-(step + 1)) + 1u;
} }
/* store the counter in place of the limit (which won't be /* store the counter in place of the limit (which won't be
needed anymore */ needed anymore) */
setivalue(plimit, l_castU2S(count)); setivalue(plimit, l_castU2S(count));
} }
} }
@ -634,7 +634,8 @@ static void copy2buff (StkId top, int n, char *buff) {
** from 'L->top - total' up to 'L->top - 1'. ** from 'L->top - total' up to 'L->top - 1'.
*/ */
void luaV_concat (lua_State *L, int total) { void luaV_concat (lua_State *L, int total) {
lua_assert(total >= 2); if (total == 1)
return; /* "all" values already concatenated */
do { do {
StkId top = L->top; StkId top = L->top;
int n = 2; /* number of elements handled in this pass (at least 2) */ int n = 2; /* number of elements handled in this pass (at least 2) */
@ -840,10 +841,8 @@ void luaV_finishOp (lua_State *L) {
int a = GETARG_A(inst); /* first element to concatenate */ int a = GETARG_A(inst); /* first element to concatenate */
int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
setobjs2s(L, top - 2, top); /* put TM result in proper position */ setobjs2s(L, top - 2, top); /* put TM result in proper position */
if (total > 1) { /* are there elements to concat? */
L->top = top - 1; /* top is one after last element (at top-2) */ L->top = top - 1; /* top is one after last element (at top-2) */
luaV_concat(L, total); /* concat them (may yield again) */ luaV_concat(L, total); /* concat them (may yield again) */
}
break; break;
} }
default: { default: {
@ -1093,18 +1092,14 @@ void luaV_finishOp (lua_State *L) {
#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) #define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
/* /*
** Protect code that will finish the loop (returns) or can only raise ** Protect code that can only raise errors. (That is, it cannnot change
** errors. (That is, it will not return to the interpreter main loop ** the stack or hooks.)
** after changing the stack or hooks.)
*/ */
#define halfProtect(exp) (savestate(L,ci), (exp)) #define halfProtect(exp) (savestate(L,ci), (exp))
/* idem, but without changing the stack */ /* 'c' is the limit of live values in the stack */
#define halfProtectNT(exp) (savepc(L), (exp))
#define checkGC(L,c) \ #define checkGC(L,c) \
{ luaC_condGC(L, L->top = (c), /* limit of live values */ \ { luaC_condGC(L, (savepc(L), L->top = (c)), \
updatetrap(ci)); \ updatetrap(ci)); \
luai_threadyield(L); } luai_threadyield(L); }
@ -1133,17 +1128,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
#if LUA_USE_JUMPTABLE #if LUA_USE_JUMPTABLE
#include "ljumptab.h" #include "ljumptab.h"
#endif #endif
tailcall: startfunc:
trap = L->hookmask; trap = L->hookmask;
returning: /* trap already set */
cl = clLvalue(s2v(ci->func)); cl = clLvalue(s2v(ci->func));
k = cl->p->k; k = cl->p->k;
pc = ci->u.l.savedpc; pc = ci->u.l.savedpc;
if (trap) { if (trap) {
if (pc == cl->p->code) { /* first instruction (not resuming)? */
if (cl->p->is_vararg) if (cl->p->is_vararg)
trap = 0; /* hooks will start after VARARGPREP instruction */ trap = 0; /* hooks will start after VARARGPREP instruction */
else if (pc == cl->p->code) /* first instruction (not resuming)? */ else /* check 'call' hook */
luaD_hookcall(L, ci); luaD_hookcall(L, ci);
ci->u.l.trap = 1; /* there may be other hooks */ }
ci->u.l.trap = 1; /* assume trap is on, for now */
} }
base = ci->func + 1; base = ci->func + 1;
/* main loop of interpreter */ /* main loop of interpreter */
@ -1152,7 +1150,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
StkId ra; /* instruction's A register */ StkId ra; /* instruction's A register */
vmfetch(); vmfetch();
lua_assert(base == ci->func + 1); lua_assert(base == ci->func + 1);
lua_assert(base <= L->top && L->top < L->stack + L->stacksize); lua_assert(base <= L->top && L->top < L->stack_last);
/* invalidate top for instructions not expecting it */ /* invalidate top for instructions not expecting it */
lua_assert(isIT(i) || (cast_void(L->top = base), 1)); lua_assert(isIT(i) || (cast_void(L->top = base), 1));
vmdispatch (GET_OPCODE(i)) { vmdispatch (GET_OPCODE(i)) {
@ -1607,24 +1605,32 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_CALL) { vmcase(OP_CALL) {
CallInfo *newci;
int b = GETARG_B(i); int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1; int nresults = GETARG_C(i) - 1;
if (b != 0) /* fixed number of arguments? */ if (b != 0) /* fixed number of arguments? */
L->top = ra + b; /* top signals number of arguments */ L->top = ra + b; /* top signals number of arguments */
/* else previous instruction set top */ /* else previous instruction set top */
ProtectNT(luaD_call(L, ra, nresults)); savepc(L); /* in case of errors */
if ((newci = luaD_precall(L, ra, nresults)) == NULL)
updatetrap(ci); /* C call; nothing else to be done */
else { /* Lua call: run function in this same C frame */
ci = newci;
ci->callstatus = 0; /* call re-uses 'luaV_execute' */
goto startfunc;
}
vmbreak; vmbreak;
} }
vmcase(OP_TAILCALL) { vmcase(OP_TAILCALL) {
int b = GETARG_B(i); /* number of arguments + 1 (function) */ int b = GETARG_B(i); /* number of arguments + 1 (function) */
int nparams1 = GETARG_C(i); int nparams1 = GETARG_C(i);
/* delat is virtual 'func' - real 'func' (vararg functions) */ /* delta is virtual 'func' - real 'func' (vararg functions) */
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
if (b != 0) if (b != 0)
L->top = ra + b; L->top = ra + b;
else /* previous instruction set top */ else /* previous instruction set top */
b = cast_int(L->top - ra); b = cast_int(L->top - ra);
savepc(ci); /* some calls here can raise errors */ savepc(ci); /* several calls here can raise errors */
if (TESTARG_k(i)) { if (TESTARG_k(i)) {
/* close upvalues from current call; the compiler ensures /* close upvalues from current call; the compiler ensures
that there are no to-be-closed variables here, so this that there are no to-be-closed variables here, so this
@ -1635,19 +1641,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
while (!ttisfunction(s2v(ra))) { /* not a function? */ while (!ttisfunction(s2v(ra))) { /* not a function? */
luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
b++; /* there is now one extra argument */ b++; /* there is now one extra argument */
checkstackp(L, 1, ra); checkstackGCp(L, 1, ra);
} }
if (!ttisLclosure(s2v(ra))) { /* C function? */ if (!ttisLclosure(s2v(ra))) { /* C function? */
luaD_call(L, ra, LUA_MULTRET); /* call it */ luaD_precall(L, ra, LUA_MULTRET); /* call it */
updatetrap(ci); updatetrap(ci);
updatestack(ci); /* stack may have been relocated */ updatestack(ci); /* stack may have been relocated */
ci->func -= delta; ci->func -= delta; /* restore 'func' (if vararg) */
luaD_poscall(L, ci, cast_int(L->top - ra)); luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */
return; updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret; /* caller returns after the tail call */
} }
ci->func -= delta; ci->func -= delta; /* restore 'func' (if vararg) */
luaD_pretailcall(L, ci, ra, b); /* prepare call frame */ luaD_pretailcall(L, ci, ra, b); /* prepare call frame */
goto tailcall; goto startfunc; /* execute the callee */
} }
vmcase(OP_RETURN) { vmcase(OP_RETURN) {
int n = GETARG_B(i) - 1; /* number of results */ int n = GETARG_B(i) - 1; /* number of results */
@ -1666,12 +1673,15 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
ci->func -= ci->u.l.nextraargs + nparams1; ci->func -= ci->u.l.nextraargs + nparams1;
L->top = ra + n; /* set call for 'luaD_poscall' */ L->top = ra + n; /* set call for 'luaD_poscall' */
luaD_poscall(L, ci, n); luaD_poscall(L, ci, n);
return; updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret;
} }
vmcase(OP_RETURN0) { vmcase(OP_RETURN0) {
if (L->hookmask) { if (L->hookmask) {
L->top = ra; L->top = ra;
halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */ savepc(ci);
luaD_poscall(L, ci, 0); /* no hurry... */
trap = 1;
} }
else { /* do the 'poscall' here */ else { /* do the 'poscall' here */
int nres = ci->nresults; int nres = ci->nresults;
@ -1680,12 +1690,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
while (nres-- > 0) while (nres-- > 0)
setnilvalue(s2v(L->top++)); /* all results are nil */ setnilvalue(s2v(L->top++)); /* all results are nil */
} }
return; goto ret;
} }
vmcase(OP_RETURN1) { vmcase(OP_RETURN1) {
if (L->hookmask) { if (L->hookmask) {
L->top = ra + 1; L->top = ra + 1;
halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */ savepc(ci);
luaD_poscall(L, ci, 1); /* no hurry... */
trap = 1;
} }
else { /* do the 'poscall' here */ else { /* do the 'poscall' here */
int nres = ci->nresults; int nres = ci->nresults;
@ -1699,7 +1711,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
setnilvalue(s2v(L->top++)); setnilvalue(s2v(L->top++));
} }
} }
return; ret: /* return from a Lua function */
if (ci->callstatus & CIST_FRESH)
return; /* end this frame */
else {
ci = ci->previous;
goto returning; /* continue running caller in this frame */
}
} }
vmcase(OP_FORLOOP) { vmcase(OP_FORLOOP) {
if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
@ -1792,11 +1810,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_VARARGPREP) { vmcase(OP_VARARGPREP) {
luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p); ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
updatetrap(ci);
if (trap) { if (trap) {
luaD_hookcall(L, ci); luaD_hookcall(L, ci);
L->oldpc = pc + 1; /* next opcode will be seen as a "new" line */ L->oldpc = 1; /* next opcode will be seen as a "new" line */
} }
updatebase(ci); /* function has new base after adjustment */ updatebase(ci); /* function has new base after adjustment */
vmbreak; vmbreak;

Binary file not shown.

Binary file not shown.