No more LUA_ERRGCMM errors
Errors in finalizers (__gc metamethods) are never propagated. Instead, they generate a warning.
This commit is contained in:
4
lapi.c
4
lapi.c
@@ -1276,10 +1276,8 @@ void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) {
|
|||||||
|
|
||||||
|
|
||||||
void lua_warning (lua_State *L, const char *msg) {
|
void lua_warning (lua_State *L, const char *msg) {
|
||||||
lua_WarnFunction wf = G(L)->warnf;
|
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
if (wf != NULL)
|
luaE_warning(L, msg);
|
||||||
wf(&G(L)->ud_warn, msg);
|
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
28
lgc.c
28
lgc.c
@@ -824,7 +824,7 @@ static void dothecall (lua_State *L, void *ud) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void GCTM (lua_State *L, int propagateerrors) {
|
static void GCTM (lua_State *L) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
const TValue *tm;
|
const TValue *tm;
|
||||||
TValue v;
|
TValue v;
|
||||||
@@ -845,15 +845,13 @@ static void GCTM (lua_State *L, int propagateerrors) {
|
|||||||
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
||||||
L->allowhook = oldah; /* restore hooks */
|
L->allowhook = oldah; /* restore hooks */
|
||||||
g->gcrunning = running; /* restore state */
|
g->gcrunning = running; /* restore state */
|
||||||
if (status != LUA_OK && propagateerrors) { /* error while running __gc? */
|
if (status != LUA_OK) { /* error while running __gc? */
|
||||||
if (status == LUA_ERRRUN) { /* is there an error object? */
|
const char *msg = (ttisstring(s2v(L->top - 1)))
|
||||||
const char *msg = (ttisstring(s2v(L->top - 1)))
|
? svalue(s2v(L->top - 1))
|
||||||
? svalue(s2v(L->top - 1))
|
: "error object is not a string";
|
||||||
: "no message";
|
luaE_warning(L, "error in __gc metamethod (");
|
||||||
luaO_pushfstring(L, "error in __gc metamethod (%s)", msg);
|
luaE_warning(L, msg);
|
||||||
status = LUA_ERRGCMM; /* error in __gc metamethod */
|
luaE_warning(L, ")\n");
|
||||||
}
|
|
||||||
luaD_throw(L, status); /* re-throw error */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -866,7 +864,7 @@ static int runafewfinalizers (lua_State *L, int n) {
|
|||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < n && g->tobefnz; i++)
|
for (i = 0; i < n && g->tobefnz; i++)
|
||||||
GCTM(L, 1); /* call one finalizer */
|
GCTM(L); /* call one finalizer */
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,10 +872,10 @@ static int runafewfinalizers (lua_State *L, int n) {
|
|||||||
/*
|
/*
|
||||||
** call all pending finalizers
|
** call all pending finalizers
|
||||||
*/
|
*/
|
||||||
static void callallpendingfinalizers (lua_State *L, int propagateerrors) {
|
static void callallpendingfinalizers (lua_State *L) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
while (g->tobefnz)
|
while (g->tobefnz)
|
||||||
GCTM(L, propagateerrors);
|
GCTM(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1124,7 +1122,7 @@ static void finishgencycle (lua_State *L, global_State *g) {
|
|||||||
checkSizes(L, g);
|
checkSizes(L, g);
|
||||||
g->gcstate = GCSpropagate; /* skip restart */
|
g->gcstate = GCSpropagate; /* skip restart */
|
||||||
if (!g->gcemergency)
|
if (!g->gcemergency)
|
||||||
callallpendingfinalizers(L, 1);
|
callallpendingfinalizers(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1334,7 +1332,7 @@ void luaC_freeallobjects (lua_State *L) {
|
|||||||
luaC_changemode(L, KGC_INC);
|
luaC_changemode(L, KGC_INC);
|
||||||
separatetobefnz(g, 1); /* separate all objects with finalizers */
|
separatetobefnz(g, 1); /* separate all objects with finalizers */
|
||||||
lua_assert(g->finobj == NULL);
|
lua_assert(g->finobj == NULL);
|
||||||
callallpendingfinalizers(L, 0);
|
callallpendingfinalizers(L);
|
||||||
deletelist(L, g->allgc, obj2gco(g->mainthread));
|
deletelist(L, g->allgc, obj2gco(g->mainthread));
|
||||||
deletelist(L, g->finobj, NULL);
|
deletelist(L, g->finobj, NULL);
|
||||||
deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
|
deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
|
||||||
|
|||||||
7
lstate.c
7
lstate.c
@@ -409,3 +409,10 @@ LUA_API void lua_close (lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void luaE_warning (lua_State *L, const char *msg) {
|
||||||
|
lua_WarnFunction wf = G(L)->warnf;
|
||||||
|
if (wf != NULL)
|
||||||
|
wf(&G(L)->ud_warn, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
lstate.h
1
lstate.h
@@ -316,6 +316,7 @@ 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_enterCcall (lua_State *L);
|
||||||
|
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg);
|
||||||
|
|
||||||
|
|
||||||
#define luaE_exitCcall(L) ((L)->nCcalls--)
|
#define luaE_exitCcall(L) ((L)->nCcalls--)
|
||||||
|
|||||||
59
ltests.c
59
ltests.c
@@ -63,7 +63,11 @@ static void pushobject (lua_State *L, const TValue *o) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void badexit (void) {
|
static void badexit (const char *fmt, ...) {
|
||||||
|
va_list argp;
|
||||||
|
va_start(argp, fmt);
|
||||||
|
vfprintf(stderr, fmt, argp);
|
||||||
|
va_end(argp);
|
||||||
/* avoid assertion failures when exiting */
|
/* avoid assertion failures when exiting */
|
||||||
l_memcontrol.numblocks = l_memcontrol.total = 0;
|
l_memcontrol.numblocks = l_memcontrol.total = 0;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -71,9 +75,9 @@ static void badexit (void) {
|
|||||||
|
|
||||||
|
|
||||||
static int tpanic (lua_State *L) {
|
static int tpanic (lua_State *L) {
|
||||||
fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
|
return (badexit("PANIC: unprotected error in call to Lua API (%s)\n",
|
||||||
lua_tostring(L, -1));
|
lua_tostring(L, -1)),
|
||||||
return (badexit(), 0); /* do not return to Lua */
|
0); /* do not return to Lua */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -83,16 +87,47 @@ static int islast (const char *message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Warning function for tests. Fist, it concatenates all parts of
|
||||||
|
** a warning in buffer 'buff'. Then:
|
||||||
|
** messages starting with '#' are shown on standard output (used to
|
||||||
|
** test explicit warnings);
|
||||||
|
** messages containing '@' are stored in global '_WARN' (used to test
|
||||||
|
** errors that generate warnings);
|
||||||
|
** other messages abort the tests (they represent real warning conditions;
|
||||||
|
** the standard tests should not generate these conditions unexpectedly).
|
||||||
|
*/
|
||||||
static void warnf (void **pud, const char *msg) {
|
static void warnf (void **pud, const char *msg) {
|
||||||
if (*pud == NULL) /* continuation line? */
|
static char buff[200]; /* should be enough for tests... */
|
||||||
printf("%s", msg); /* print it */
|
static int cont = 0; /* message to be continued */
|
||||||
else if (msg[0] == '*') /* expected warning? */
|
if (cont) { /* continuation? */
|
||||||
printf("Expected Lua warning: %s", msg + 1); /* print without the star */
|
if (strlen(msg) >= sizeof(buff) - strlen(buff))
|
||||||
else { /* a real warning; should not happen during tests */
|
badexit("warnf-buffer overflow");
|
||||||
fprintf(stderr, "Warning in test mode (%s), aborting...\n", msg);
|
strcat(buff, msg); /* add new message to current warning */
|
||||||
badexit();
|
}
|
||||||
|
else { /* new warning */
|
||||||
|
if (strlen(msg) >= sizeof(buff))
|
||||||
|
badexit("warnf-buffer overflow");
|
||||||
|
strcpy(buff, msg); /* start a new warning */
|
||||||
|
}
|
||||||
|
if (!islast(msg)) /* message not finished yet? */
|
||||||
|
cont = 1; /* wait for more */
|
||||||
|
else { /* handle message */
|
||||||
|
cont = 0; /* prepare for next message */
|
||||||
|
if (buff[0] == '#') /* expected warning? */
|
||||||
|
printf("Expected Lua warning: %s", buff); /* print it */
|
||||||
|
else if (strchr(buff, '@') != NULL) { /* warning for test purposes? */
|
||||||
|
lua_State *L = cast(lua_State *, *pud);
|
||||||
|
lua_unlock(L);
|
||||||
|
lua_pushstring(L, buff);
|
||||||
|
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
|
||||||
|
lua_lock(L);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { /* a real warning; should not happen during tests */
|
||||||
|
badexit("Unexpected warning in test mode: %s\naborting...\n", buff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*pud = islast(msg) ? pud : NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
3
lua.h
3
lua.h
@@ -51,8 +51,7 @@
|
|||||||
#define LUA_ERRRUN 2
|
#define LUA_ERRRUN 2
|
||||||
#define LUA_ERRSYNTAX 3
|
#define LUA_ERRSYNTAX 3
|
||||||
#define LUA_ERRMEM 4
|
#define LUA_ERRMEM 4
|
||||||
#define LUA_ERRGCMM 5
|
#define LUA_ERRERR 5
|
||||||
#define LUA_ERRERR 6
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct lua_State lua_State;
|
typedef struct lua_State lua_State;
|
||||||
|
|||||||
@@ -722,8 +722,6 @@ Lua calls the finalizers of all objects marked for finalization,
|
|||||||
following the reverse order that they were marked.
|
following the reverse order that they were marked.
|
||||||
If any finalizer marks objects for collection during that phase,
|
If any finalizer marks objects for collection during that phase,
|
||||||
these marks have no effect.
|
these marks have no effect.
|
||||||
If any finalizer raises an error during that phase,
|
|
||||||
its execution is interrupted but the error is ignored.
|
|
||||||
|
|
||||||
Finalizers cannot yield.
|
Finalizers cannot yield.
|
||||||
|
|
||||||
@@ -2645,8 +2643,7 @@ by looking only at its arguments
|
|||||||
The third field, @T{x},
|
The third field, @T{x},
|
||||||
tells whether the function may raise errors:
|
tells whether the function may raise errors:
|
||||||
@Char{-} means the function never raises any error;
|
@Char{-} means the function never raises any error;
|
||||||
@Char{m} means the function may raise out-of-memory errors
|
@Char{m} means the function may raise only out-of-memory errors;
|
||||||
and errors running a finalizer;
|
|
||||||
@Char{v} means the function may raise the errors explained in the text;
|
@Char{v} means the function may raise the errors explained in the text;
|
||||||
@Char{e} means the function can run arbitrary Lua code,
|
@Char{e} means the function can run arbitrary Lua code,
|
||||||
either directly or through metamethods,
|
either directly or through metamethods,
|
||||||
@@ -3364,12 +3361,6 @@ syntax error during precompilation;}
|
|||||||
@item{@Lid{LUA_ERRMEM}|
|
@item{@Lid{LUA_ERRMEM}|
|
||||||
@x{memory allocation (out-of-memory) error};}
|
@x{memory allocation (out-of-memory) error};}
|
||||||
|
|
||||||
@item{@Lid{LUA_ERRGCMM}|
|
|
||||||
error while running a @idx{__gc} metamethod.
|
|
||||||
(This error has no relation with the chunk being loaded.
|
|
||||||
It is generated by the garbage collector.)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
The @id{lua_load} function uses a user-supplied @id{reader} function
|
The @id{lua_load} function uses a user-supplied @id{reader} function
|
||||||
@@ -3564,13 +3555,6 @@ For such errors, Lua does not call the @x{message handler}.
|
|||||||
error while running the @x{message handler}.
|
error while running the @x{message handler}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@defid{LUA_ERRGCMM}|
|
|
||||||
error while running a @idx{__gc} metamethod.
|
|
||||||
For such errors, Lua does not call the @x{message handler}
|
|
||||||
(as this kind of error typically has no relation
|
|
||||||
with the function being called).
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6298,6 +6282,8 @@ The current value of this variable is @St{Lua 5.4}.
|
|||||||
@LibEntry{warn (message)|
|
@LibEntry{warn (message)|
|
||||||
|
|
||||||
Emits a warning with the given message.
|
Emits a warning with the given message.
|
||||||
|
Note that messages not ending with an end-of-line
|
||||||
|
are assumed to be continued by the message in the next call.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8773,6 +8759,12 @@ so there is no need to check whether they are using the same
|
|||||||
address space.)
|
address space.)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@item{
|
||||||
|
The constant @Lid{LUA_ERRGCMM} was removed.
|
||||||
|
Errors in finalizers are never propagated;
|
||||||
|
instead, they generate a warning.
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,12 +190,17 @@ assert(dofile('verybig.lua', true) == 10); collectgarbage()
|
|||||||
dofile('files.lua')
|
dofile('files.lua')
|
||||||
|
|
||||||
if #msgs > 0 then
|
if #msgs > 0 then
|
||||||
warn("*tests not performed:\n ")
|
warn("#tests not performed:\n ")
|
||||||
for i=1,#msgs do
|
for i=1,#msgs do
|
||||||
warn(msgs[i]); warn("\n ")
|
warn(msgs[i]); warn("\n ")
|
||||||
end
|
end
|
||||||
|
warn("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print("(there should be two warnings now)")
|
||||||
|
warn("#This is "); warn("an expected"); warn(" warning\n")
|
||||||
|
warn("#This is"); warn(" another one\n")
|
||||||
|
|
||||||
-- no test module should define 'debug'
|
-- no test module should define 'debug'
|
||||||
assert(debug == nil)
|
assert(debug == nil)
|
||||||
|
|
||||||
@@ -219,10 +224,6 @@ local _G, showmem, print, format, clock, time, difftime, assert, open =
|
|||||||
local fname = T and "time-debug.txt" or "time.txt"
|
local fname = T and "time-debug.txt" or "time.txt"
|
||||||
local lasttime
|
local lasttime
|
||||||
|
|
||||||
|
|
||||||
warn("*This is "); warn("an expected"); warn(" warning\n")
|
|
||||||
warn("*This is"); warn(" another one\n")
|
|
||||||
|
|
||||||
if not usertests then
|
if not usertests then
|
||||||
-- open file with time of last performed test
|
-- open file with time of last performed test
|
||||||
local f = io.open(fname)
|
local f = io.open(fname)
|
||||||
|
|||||||
@@ -114,13 +114,12 @@ end
|
|||||||
|
|
||||||
-- testing warnings
|
-- testing warnings
|
||||||
T.testC([[
|
T.testC([[
|
||||||
warning "*This "
|
warning "#This shold be a"
|
||||||
warning "warning "
|
warning " single "
|
||||||
warning "should be in a"
|
warning "warning
|
||||||
warning " single line
|
|
||||||
"
|
"
|
||||||
warning "*This should be "
|
warning "#This should be "
|
||||||
warning "another warning
|
warning "another one
|
||||||
"
|
"
|
||||||
]])
|
]])
|
||||||
|
|
||||||
@@ -896,24 +895,15 @@ do -- testing errors during GC
|
|||||||
a[i] = T.newuserdata(i) -- creates several udata
|
a[i] = T.newuserdata(i) -- creates several udata
|
||||||
end
|
end
|
||||||
for i=1,20,2 do -- mark half of them to raise errors during GC
|
for i=1,20,2 do -- mark half of them to raise errors during GC
|
||||||
debug.setmetatable(a[i], {__gc = function (x) error("error inside gc") end})
|
debug.setmetatable(a[i],
|
||||||
|
{__gc = function (x) error("@expected error in gc") end})
|
||||||
end
|
end
|
||||||
for i=2,20,2 do -- mark the other half to count and to create more garbage
|
for i=2,20,2 do -- mark the other half to count and to create more garbage
|
||||||
debug.setmetatable(a[i], {__gc = function (x) load("A=A+1")() end})
|
debug.setmetatable(a[i], {__gc = function (x) load("A=A+1")() end})
|
||||||
end
|
end
|
||||||
|
a = nil
|
||||||
_G.A = 0
|
_G.A = 0
|
||||||
a = 0
|
collectgarbage()
|
||||||
while 1 do
|
|
||||||
local stat, msg = pcall(collectgarbage)
|
|
||||||
if stat then
|
|
||||||
break -- stop when no more errors
|
|
||||||
else
|
|
||||||
a = a + 1
|
|
||||||
assert(string.find(msg, "__gc"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert(a == 10) -- number of errors
|
|
||||||
|
|
||||||
assert(A == 10) -- number of normal collections
|
assert(A == 10) -- number of normal collections
|
||||||
collectgarbage("restart")
|
collectgarbage("restart")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -353,40 +353,36 @@ GC()
|
|||||||
|
|
||||||
|
|
||||||
-- testing errors during GC
|
-- testing errors during GC
|
||||||
do
|
if T then
|
||||||
collectgarbage("stop") -- stop collection
|
collectgarbage("stop") -- stop collection
|
||||||
local u = {}
|
local u = {}
|
||||||
local s = {}; setmetatable(s, {__mode = 'k'})
|
local s = {}; setmetatable(s, {__mode = 'k'})
|
||||||
setmetatable(u, {__gc = function (o)
|
setmetatable(u, {__gc = function (o)
|
||||||
local i = s[o]
|
local i = s[o]
|
||||||
s[i] = true
|
s[i] = true
|
||||||
assert(not s[i - 1]) -- check proper finalization order
|
assert(not s[i - 1]) -- check proper finalization order
|
||||||
if i == 8 then error("here") end -- error during GC
|
if i == 8 then error("@expected@") end -- error during GC
|
||||||
end})
|
end})
|
||||||
|
|
||||||
for i = 6, 10 do
|
for i = 6, 10 do
|
||||||
local n = setmetatable({}, getmetatable(u))
|
local n = setmetatable({}, getmetatable(u))
|
||||||
s[n] = i
|
s[n] = i
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(not pcall(collectgarbage))
|
collectgarbage()
|
||||||
for i = 8, 10 do assert(s[i]) end
|
assert(string.find(_WARN, "error in __gc metamethod"))
|
||||||
|
assert(string.match(_WARN, "@(.-)@") == "expected")
|
||||||
|
for i = 8, 10 do assert(s[i]) end
|
||||||
|
|
||||||
for i = 1, 5 do
|
for i = 1, 5 do
|
||||||
local n = setmetatable({}, getmetatable(u))
|
local n = setmetatable({}, getmetatable(u))
|
||||||
s[n] = i
|
s[n] = i
|
||||||
end
|
end
|
||||||
|
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
for i = 1, 10 do assert(s[i]) end
|
for i = 1, 10 do assert(s[i]) end
|
||||||
|
|
||||||
getmetatable(u).__gc = false
|
getmetatable(u).__gc = false
|
||||||
|
|
||||||
|
|
||||||
-- __gc errors with non-string messages
|
|
||||||
setmetatable({}, {__gc = function () error{} end})
|
|
||||||
local a, b = pcall(collectgarbage)
|
|
||||||
assert(not a and type(b) == "string" and string.find(b, "error in __gc"))
|
|
||||||
|
|
||||||
end
|
end
|
||||||
print '+'
|
print '+'
|
||||||
@@ -478,9 +474,11 @@ end
|
|||||||
|
|
||||||
|
|
||||||
-- errors during collection
|
-- errors during collection
|
||||||
u = setmetatable({}, {__gc = function () error "!!!" end})
|
if T then
|
||||||
u = nil
|
u = setmetatable({}, {__gc = function () error "@expected error" end})
|
||||||
assert(not pcall(collectgarbage))
|
u = nil
|
||||||
|
collectgarbage()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if not _soft then
|
if not _soft then
|
||||||
@@ -645,11 +643,26 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- create several objects to raise errors when collected while closing state
|
-- create several objects to raise errors when collected while closing state
|
||||||
do
|
if T then
|
||||||
local mt = {__gc = function (o) return o + 1 end}
|
local error, assert, warn, find = error, assert, warn, string.find
|
||||||
for i = 1,10 do
|
local n = 0
|
||||||
|
local lastmsg
|
||||||
|
local mt = {__gc = function (o)
|
||||||
|
n = n + 1
|
||||||
|
assert(n == o[1])
|
||||||
|
if n == 1 then
|
||||||
|
_WARN = nil
|
||||||
|
elseif n == 2 then
|
||||||
|
assert(find(_WARN, "@expected warning"))
|
||||||
|
lastmsg = _WARN -- get message from previous error (first 'o')
|
||||||
|
else
|
||||||
|
assert(lastmsg == _WARN) -- subsequent error messages are equal
|
||||||
|
end
|
||||||
|
error"@expected warning"
|
||||||
|
end}
|
||||||
|
for i = 10, 1, -1 do
|
||||||
-- create object and preserve it until the end
|
-- create object and preserve it until the end
|
||||||
table.insert(___Glob, setmetatable({}, mt))
|
table.insert(___Glob, setmetatable({i}, mt))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user