diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2010-11-26 12:32:31 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2010-11-26 12:32:31 -0200 |
commit | 9b7a12c46d85666e0e60efd79b39495589e497e8 (patch) | |
tree | c69acde3d3e300fa0db87004045e7599cbbca7a2 | |
parent | 5b33e398557d7afad4a916bb483e9894e4b9b3d4 (diff) | |
download | lua-9b7a12c46d85666e0e60efd79b39495589e497e8.tar.gz lua-9b7a12c46d85666e0e60efd79b39495589e497e8.tar.bz2 lua-9b7a12c46d85666e0e60efd79b39495589e497e8.zip |
finalizers (__gc) for tables
-rw-r--r-- | lapi.c | 5 | ||||
-rw-r--r-- | lgc.c | 72 | ||||
-rw-r--r-- | lgc.h | 4 | ||||
-rw-r--r-- | lobject.h | 6 | ||||
-rw-r--r-- | lstate.c | 4 | ||||
-rw-r--r-- | lstate.h | 8 | ||||
-rw-r--r-- | ltests.c | 21 |
7 files changed, 63 insertions, 57 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lapi.c,v 2.139 2010/10/25 20:31:11 roberto Exp roberto $ | 2 | ** $Id: lapi.c,v 2.141 2010/11/18 19:15:00 roberto Exp roberto $ |
3 | ** Lua API | 3 | ** Lua API |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -760,13 +760,14 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { | |||
760 | hvalue(obj)->metatable = mt; | 760 | hvalue(obj)->metatable = mt; |
761 | if (mt) | 761 | if (mt) |
762 | luaC_objbarrierback(L, gcvalue(obj), mt); | 762 | luaC_objbarrierback(L, gcvalue(obj), mt); |
763 | luaC_checkfinalizer(L, gcvalue(obj), mt); | ||
763 | break; | 764 | break; |
764 | } | 765 | } |
765 | case LUA_TUSERDATA: { | 766 | case LUA_TUSERDATA: { |
766 | uvalue(obj)->metatable = mt; | 767 | uvalue(obj)->metatable = mt; |
767 | if (mt) { | 768 | if (mt) { |
768 | luaC_objbarrier(L, rawuvalue(obj), mt); | 769 | luaC_objbarrier(L, rawuvalue(obj), mt); |
769 | luaC_checkfinalizer(L, rawuvalue(obj)); | 770 | luaC_checkfinalizer(L, gcvalue(obj), mt); |
770 | } | 771 | } |
771 | break; | 772 | break; |
772 | } | 773 | } |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lgc.c,v 2.102 2010/09/03 14:14:01 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.104 2010/11/18 19:15:00 roberto Exp roberto $ |
3 | ** Garbage Collector | 3 | ** Garbage Collector |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -68,7 +68,7 @@ | |||
68 | #define stringmark(s) ((void)((s) && resetbits((s)->tsv.marked, WHITEBITS))) | 68 | #define stringmark(s) ((void)((s) && resetbits((s)->tsv.marked, WHITEBITS))) |
69 | 69 | ||
70 | 70 | ||
71 | #define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) | 71 | #define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT) |
72 | 72 | ||
73 | #define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) | 73 | #define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) |
74 | 74 | ||
@@ -114,12 +114,11 @@ static void removeentry (Node *n) { | |||
114 | */ | 114 | */ |
115 | static int iscleared (const TValue *o, int iskey) { | 115 | static int iscleared (const TValue *o, int iskey) { |
116 | if (!iscollectable(o)) return 0; | 116 | if (!iscollectable(o)) return 0; |
117 | if (ttisstring(o)) { | 117 | else if (ttisstring(o)) { |
118 | stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ | 118 | stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ |
119 | return 0; | 119 | return 0; |
120 | } | 120 | } |
121 | return iswhite(gcvalue(o)) || | 121 | else return iswhite(gcvalue(o)) || (!iskey && isfinalized(gcvalue(o))); |
122 | (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); | ||
123 | } | 122 | } |
124 | 123 | ||
125 | 124 | ||
@@ -687,19 +686,18 @@ static void checkSizes (lua_State *L) { | |||
687 | } | 686 | } |
688 | 687 | ||
689 | 688 | ||
690 | static Udata *udata2finalize (global_State *g) { | 689 | static GCObject *udata2finalize (global_State *g) { |
691 | GCObject *o = g->tobefnz; /* get first element */ | 690 | GCObject *o = g->tobefnz; /* get first element */ |
692 | Udata *u = rawgco2u(o); | 691 | lua_assert(isfinalized(o)); |
693 | lua_assert(isfinalized(&u->uv)); | ||
694 | lua_assert(!isold(o)); | 692 | lua_assert(!isold(o)); |
695 | g->tobefnz = u->uv.next; /* remove it from 'tobefnz' list */ | 693 | g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */ |
696 | u->uv.next = g->allgc; /* return it to 'allgc' list */ | 694 | gch(o)->next = g->allgc; /* return it to 'allgc' list */ |
697 | g->allgc = o; | 695 | g->allgc = o; |
698 | resetbit(u->uv.marked, SEPARATED); /* mark that it is not in 'tobefnz' */ | 696 | resetbit(gch(o)->marked, SEPARATED); /* mark that it is not in 'tobefnz' */ |
699 | resetoldbit(o); /* see MOVE OLD rule */ | 697 | resetoldbit(o); /* see MOVE OLD rule */ |
700 | if (!keepinvariant(g)) /* not keeping invariant? */ | 698 | if (!keepinvariant(g)) /* not keeping invariant? */ |
701 | makewhite(g, o); /* "sweep" object */ | 699 | makewhite(g, o); /* "sweep" object */ |
702 | return u; | 700 | return o; |
703 | } | 701 | } |
704 | 702 | ||
705 | 703 | ||
@@ -711,8 +709,10 @@ static void dothecall (lua_State *L, void *ud) { | |||
711 | 709 | ||
712 | static void GCTM (lua_State *L, int propagateerrors) { | 710 | static void GCTM (lua_State *L, int propagateerrors) { |
713 | global_State *g = G(L); | 711 | global_State *g = G(L); |
714 | Udata *udata = udata2finalize(g); | 712 | const TValue *tm; |
715 | const TValue *tm = gfasttm(g, udata->uv.metatable, TM_GC); | 713 | TValue v; |
714 | setgcovalue(L, &v, udata2finalize(g)); | ||
715 | tm = luaT_gettmbyobj(L, &v, TM_GC); | ||
716 | if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ | 716 | if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ |
717 | int status; | 717 | int status; |
718 | lu_byte oldah = L->allowhook; | 718 | lu_byte oldah = L->allowhook; |
@@ -720,7 +720,7 @@ static void GCTM (lua_State *L, int propagateerrors) { | |||
720 | L->allowhook = 0; /* stop debug hooks during GC tag method */ | 720 | L->allowhook = 0; /* stop debug hooks during GC tag method */ |
721 | stopgc(g); /* avoid GC steps */ | 721 | stopgc(g); /* avoid GC steps */ |
722 | setobj2s(L, L->top, tm); /* push finalizer... */ | 722 | setobj2s(L, L->top, tm); /* push finalizer... */ |
723 | setuvalue(L, L->top+1, udata); /* ... and its argument */ | 723 | setobj2s(L, L->top + 1, &v); /* ... and its argument */ |
724 | L->top += 2; /* and (next line) call the finalizer */ | 724 | L->top += 2; /* and (next line) call the finalizer */ |
725 | status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); | 725 | status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); |
726 | L->allowhook = oldah; /* restore hooks */ | 726 | L->allowhook = oldah; /* restore hooks */ |
@@ -738,25 +738,25 @@ static void GCTM (lua_State *L, int propagateerrors) { | |||
738 | 738 | ||
739 | 739 | ||
740 | /* | 740 | /* |
741 | ** move all unreachable udata that need finalization from list 'udgc' to | 741 | ** move all unreachable objects that need finalization from list 'finobj' |
742 | ** list 'tobefnz' | 742 | ** to list 'tobefnz' |
743 | */ | 743 | */ |
744 | void luaC_separateudata (lua_State *L, int all) { | 744 | void luaC_separateudata (lua_State *L, int all) { |
745 | global_State *g = G(L); | 745 | global_State *g = G(L); |
746 | GCObject **p = &g->udgc; | 746 | GCObject **p = &g->finobj; |
747 | GCObject *curr; | 747 | GCObject *curr; |
748 | GCObject **lastnext = &g->tobefnz; | 748 | GCObject **lastnext = &g->tobefnz; |
749 | /* find last 'next' field in 'tobefnz' list (to add elements in its end) */ | 749 | /* find last 'next' field in 'tobefnz' list (to add elements in its end) */ |
750 | while (*lastnext != NULL) | 750 | while (*lastnext != NULL) |
751 | lastnext = &gch(*lastnext)->next; | 751 | lastnext = &gch(*lastnext)->next; |
752 | while ((curr = *p) != NULL) { /* traverse all finalizable objects */ | 752 | while ((curr = *p) != NULL) { /* traverse all finalizable objects */ |
753 | lua_assert(gch(curr)->tt == LUA_TUSERDATA && !isfinalized(gco2u(curr))); | 753 | lua_assert(!isfinalized(curr)); |
754 | lua_assert(testbit(gch(curr)->marked, SEPARATED)); | 754 | lua_assert(testbit(gch(curr)->marked, SEPARATED)); |
755 | if (!(all || iswhite(curr))) /* not being collected? */ | 755 | if (!(all || iswhite(curr))) /* not being collected? */ |
756 | p = &gch(curr)->next; /* don't bother with it */ | 756 | p = &gch(curr)->next; /* don't bother with it */ |
757 | else { | 757 | else { |
758 | l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ | 758 | l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ |
759 | *p = gch(curr)->next; /* remove 'curr' from 'udgc' list */ | 759 | *p = gch(curr)->next; /* remove 'curr' from 'finobj' list */ |
760 | gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */ | 760 | gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */ |
761 | *lastnext = curr; | 761 | *lastnext = curr; |
762 | lastnext = &gch(curr)->next; | 762 | lastnext = &gch(curr)->next; |
@@ -766,23 +766,23 @@ void luaC_separateudata (lua_State *L, int all) { | |||
766 | 766 | ||
767 | 767 | ||
768 | /* | 768 | /* |
769 | ** if userdata 'u' has a finalizer, remove it from 'allgc' list (must | 769 | ** if object 'o' has a finalizer, remove it from 'allgc' list (must |
770 | ** search the list to find it) and link it in 'udgc' list. | 770 | ** search the list to find it) and link it in 'finobj' list. |
771 | */ | 771 | */ |
772 | void luaC_checkfinalizer (lua_State *L, Udata *u) { | 772 | void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { |
773 | global_State *g = G(L); | 773 | global_State *g = G(L); |
774 | if (testbit(u->uv.marked, SEPARATED) || /* udata is already separated... */ | 774 | if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */ |
775 | isfinalized(&u->uv) || /* ... or is finalized... */ | 775 | isfinalized(o) || /* ... or is finalized... */ |
776 | gfasttm(g, u->uv.metatable, TM_GC) == NULL) /* or has no finalizer? */ | 776 | gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ |
777 | return; /* nothing to be done */ | 777 | return; /* nothing to be done */ |
778 | else { /* move 'u' to 'udgc' list */ | 778 | else { /* move 'o' to 'finobj' list */ |
779 | GCObject **p; | 779 | GCObject **p; |
780 | for (p = &g->allgc; *p != obj2gco(u); p = &gch(*p)->next) ; | 780 | for (p = &g->allgc; *p != o; p = &gch(*p)->next) ; |
781 | *p = u->uv.next; /* remove 'u' from root list */ | 781 | *p = gch(o)->next; /* remove 'o' from root list */ |
782 | u->uv.next = g->udgc; /* link it in list 'udgc' */ | 782 | gch(o)->next = g->finobj; /* link it in list 'finobj' */ |
783 | g->udgc = obj2gco(u); | 783 | g->finobj = o; |
784 | l_setbit(u->uv.marked, SEPARATED); /* mark it as such */ | 784 | l_setbit(gch(o)->marked, SEPARATED); /* mark it as such */ |
785 | resetoldbit(obj2gco(u)); /* see MOVE OLD rule */ | 785 | resetoldbit(o); /* see MOVE OLD rule */ |
786 | } | 786 | } |
787 | } | 787 | } |
788 | 788 | ||
@@ -837,8 +837,8 @@ void luaC_freeallobjects (lua_State *L) { | |||
837 | /* following "white" makes all objects look dead */ | 837 | /* following "white" makes all objects look dead */ |
838 | g->currentwhite = WHITEBITS; | 838 | g->currentwhite = WHITEBITS; |
839 | g->gckind = KGC_NORMAL; | 839 | g->gckind = KGC_NORMAL; |
840 | sweepwholelist(L, &g->udgc); | 840 | sweepwholelist(L, &g->finobj); |
841 | lua_assert(g->udgc == NULL); | 841 | lua_assert(g->finobj == NULL); |
842 | sweepwholelist(L, &g->allgc); | 842 | sweepwholelist(L, &g->allgc); |
843 | lua_assert(g->allgc == NULL); | 843 | lua_assert(g->allgc == NULL); |
844 | for (i = 0; i < g->strt.size; i++) /* free all string lists */ | 844 | for (i = 0; i < g->strt.size; i++) /* free all string lists */ |
@@ -905,7 +905,7 @@ static l_mem singlestep (lua_State *L) { | |||
905 | return GCSWEEPCOST; | 905 | return GCSWEEPCOST; |
906 | } | 906 | } |
907 | else { /* no more strings to sweep */ | 907 | else { /* no more strings to sweep */ |
908 | g->sweepgc = &g->udgc; /* prepare to sweep userdata */ | 908 | g->sweepgc = &g->finobj; /* prepare to sweep finalizable objects */ |
909 | g->gcstate = GCSsweepudata; | 909 | g->gcstate = GCSsweepudata; |
910 | return 0; | 910 | return 0; |
911 | } | 911 | } |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lgc.h,v 2.43 2010/06/07 16:55:34 roberto Exp roberto $ | 2 | ** $Id: lgc.h,v 2.45 2010/11/18 19:15:00 roberto Exp $ |
3 | ** Garbage Collector | 3 | ** Garbage Collector |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -137,7 +137,7 @@ LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, | |||
137 | LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); | 137 | LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); |
138 | LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); | 138 | LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); |
139 | LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c); | 139 | LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c); |
140 | LUAI_FUNC void luaC_checkfinalizer (lua_State *L, Udata *u); | 140 | LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); |
141 | LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv); | 141 | LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv); |
142 | LUAI_FUNC void luaC_changemode (lua_State *L, int mode); | 142 | LUAI_FUNC void luaC_changemode (lua_State *L, int mode); |
143 | 143 | ||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lobject.h,v 2.41 2010/06/04 13:25:10 roberto Exp roberto $ | 2 | ** $Id: lobject.h,v 2.42 2010/07/26 15:53:23 roberto Exp roberto $ |
3 | ** Type definitions for Lua objects | 3 | ** Type definitions for Lua objects |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -153,6 +153,10 @@ typedef struct lua_TValue { | |||
153 | #define setbvalue(obj,x) \ | 153 | #define setbvalue(obj,x) \ |
154 | { TValue *i_o=(obj); i_o->value_.b=(x); i_o->tt_=LUA_TBOOLEAN; } | 154 | { TValue *i_o=(obj); i_o->value_.b=(x); i_o->tt_=LUA_TBOOLEAN; } |
155 | 155 | ||
156 | #define setgcovalue(L,obj,x) \ | ||
157 | { TValue *i_o=(obj); GCObject *i_g=(x); \ | ||
158 | i_o->value_.gc=i_g; i_o->tt_=gch(i_g)->tt; } | ||
159 | |||
156 | #define setsvalue(L,obj,x) \ | 160 | #define setsvalue(L,obj,x) \ |
157 | { TValue *i_o=(obj); \ | 161 | { TValue *i_o=(obj); \ |
158 | i_o->value_.gc=cast(GCObject *, (x)); i_o->tt_=LUA_TSTRING; \ | 162 | i_o->value_.gc=cast(GCObject *, (x)); i_o->tt_=LUA_TSTRING; \ |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lstate.c,v 2.85 2010/04/30 18:36:22 roberto Exp roberto $ | 2 | ** $Id: lstate.c,v 2.86 2010/09/03 14:14:01 roberto Exp roberto $ |
3 | ** Global State | 3 | ** Global State |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -252,7 +252,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
252 | g->version = lua_version(NULL); | 252 | g->version = lua_version(NULL); |
253 | g->gcstate = GCSpause; | 253 | g->gcstate = GCSpause; |
254 | g->allgc = NULL; | 254 | g->allgc = NULL; |
255 | g->udgc = NULL; | 255 | g->finobj = NULL; |
256 | g->tobefnz = NULL; | 256 | g->tobefnz = NULL; |
257 | g->gray = g->grayagain = NULL; | 257 | g->gray = g->grayagain = NULL; |
258 | g->weak = g->ephemeron = g->allweak = NULL; | 258 | g->weak = g->ephemeron = g->allweak = NULL; |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lstate.h,v 2.67 2010/09/30 17:21:31 roberto Exp roberto $ | 2 | ** $Id: lstate.h,v 2.68 2010/10/29 17:52:46 roberto Exp roberto $ |
3 | ** Global State | 3 | ** Global State |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -32,9 +32,9 @@ | |||
32 | ** when traversing the respective threads, but the thread may already be | 32 | ** when traversing the respective threads, but the thread may already be |
33 | ** dead, while the upvalue is still accessible through closures.) | 33 | ** dead, while the upvalue is still accessible through closures.) |
34 | ** | 34 | ** |
35 | ** Userdata with finalizers are kept in the list g->udgc. | 35 | ** Objects with finalizers are kept in the list g->finobj. |
36 | ** | 36 | ** |
37 | ** The list g->tobefnz links all userdata being finalized. | 37 | ** The list g->tobefnz links all objects being finalized. |
38 | 38 | ||
39 | */ | 39 | */ |
40 | 40 | ||
@@ -125,7 +125,7 @@ typedef struct global_State { | |||
125 | lu_byte gckind; /* kind of GC running */ | 125 | lu_byte gckind; /* kind of GC running */ |
126 | int sweepstrgc; /* position of sweep in `strt' */ | 126 | int sweepstrgc; /* position of sweep in `strt' */ |
127 | GCObject *allgc; /* list of all collectable objects */ | 127 | GCObject *allgc; /* list of all collectable objects */ |
128 | GCObject *udgc; /* list of collectable userdata with finalizers */ | 128 | GCObject *finobj; /* list of collectable objects with finalizers */ |
129 | GCObject **sweepgc; /* current position of sweep */ | 129 | GCObject **sweepgc; /* current position of sweep */ |
130 | GCObject *gray; /* list of gray objects */ | 130 | GCObject *gray; /* list of gray objects */ |
131 | GCObject *grayagain; /* list of objects to be traversed atomically */ | 131 | GCObject *grayagain; /* list of objects to be traversed atomically */ |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: ltests.c,v 2.112 2010/07/28 15:51:59 roberto Exp roberto $ | 2 | ** $Id: ltests.c,v 2.113 2010/11/16 17:43:29 roberto Exp roberto $ |
3 | ** Internal Module for Debugging of the Lua Implementation | 3 | ** Internal Module for Debugging of the Lua Implementation |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -429,20 +429,21 @@ int lua_checkmemory (lua_State *L) { | |||
429 | checkobject(g, o); | 429 | checkobject(g, o); |
430 | lua_assert(!testbit(o->gch.marked, SEPARATED)); | 430 | lua_assert(!testbit(o->gch.marked, SEPARATED)); |
431 | } | 431 | } |
432 | /* check 'udgc' list */ | 432 | /* check 'finobj' list */ |
433 | checkold(g, g->udgc); | 433 | checkold(g, g->finobj); |
434 | for (o = g->udgc; o != NULL; o = gch(o)->next) { | 434 | for (o = g->finobj; o != NULL; o = gch(o)->next) { |
435 | lua_assert(gch(o)->tt == LUA_TUSERDATA && | 435 | lua_assert(!isdead(g, o) && testbit(o->gch.marked, SEPARATED)); |
436 | !isdead(g, o) && | 436 | lua_assert(gch(o)->tt == LUA_TUSERDATA || |
437 | testbit(o->gch.marked, SEPARATED)); | 437 | gch(o)->tt == LUA_TTABLE); |
438 | checkobject(g, o); | 438 | checkobject(g, o); |
439 | } | 439 | } |
440 | /* check 'tobefnz' list */ | 440 | /* check 'tobefnz' list */ |
441 | checkold(g, g->tobefnz); | 441 | checkold(g, g->tobefnz); |
442 | for (o = g->tobefnz; o != NULL; o = gch(o)->next) { | 442 | for (o = g->tobefnz; o != NULL; o = gch(o)->next) { |
443 | lua_assert(gch(o)->tt == LUA_TUSERDATA); | 443 | lua_assert(!iswhite(o)); |
444 | lua_assert(isblack(o)); | 444 | lua_assert(!isdead(g, o) && testbit(o->gch.marked, SEPARATED)); |
445 | lua_assert(testbit(o->gch.marked, SEPARATED)); | 445 | lua_assert(gch(o)->tt == LUA_TUSERDATA || |
446 | gch(o)->tt == LUA_TTABLE); | ||
446 | } | 447 | } |
447 | /* check 'uvhead' list */ | 448 | /* check 'uvhead' list */ |
448 | for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { | 449 | for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { |