| /* |
| ** LuaJIT VM tags, values and objects. |
| ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h |
| ** |
| ** Portions taken verbatim or adapted from the Lua interpreter. |
| ** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
| */ |
| |
| #ifndef _LJ_OBJ_H |
| #define _LJ_OBJ_H |
| |
| #include "lua.h" |
| #include "lj_def.h" |
| #include "lj_arch.h" |
| |
| /* -- Memory references (32 bit address space) ---------------------------- */ |
| |
| /* Memory size. */ |
| typedef uint32_t MSize; |
| |
| /* Memory reference */ |
| typedef struct MRef { |
| uint32_t ptr32; /* Pseudo 32 bit pointer. */ |
| } MRef; |
| |
| #define mref(r, t) ((t *)(void *)(uintptr_t)(r).ptr32) |
| |
| #define setmref(r, p) ((r).ptr32 = (uint32_t)(uintptr_t)(void *)(p)) |
| #define setmrefr(r, v) ((r).ptr32 = (v).ptr32) |
| |
| /* -- GC object references (32 bit address space) ------------------------- */ |
| |
| /* GCobj reference */ |
| typedef struct GCRef { |
| uint32_t gcptr32; /* Pseudo 32 bit pointer. */ |
| } GCRef; |
| |
| /* Common GC header for all collectable objects. */ |
| #define GCHeader GCRef nextgc; uint8_t marked; uint8_t gct |
| /* This occupies 6 bytes, so use the next 2 bytes for non-32 bit fields. */ |
| |
| #define gcref(r) ((GCobj *)(uintptr_t)(r).gcptr32) |
| #define gcrefp(r, t) ((t *)(void *)(uintptr_t)(r).gcptr32) |
| #define gcrefu(r) ((r).gcptr32) |
| #define gcrefi(r) ((int32_t)(r).gcptr32) |
| #define gcrefeq(r1, r2) ((r1).gcptr32 == (r2).gcptr32) |
| #define gcnext(gc) (gcref((gc)->gch.nextgc)) |
| |
| #define setgcref(r, gc) ((r).gcptr32 = (uint32_t)(uintptr_t)&(gc)->gch) |
| #define setgcrefi(r, i) ((r).gcptr32 = (uint32_t)(i)) |
| #define setgcrefp(r, p) ((r).gcptr32 = (uint32_t)(uintptr_t)(p)) |
| #define setgcrefnull(r) ((r).gcptr32 = 0) |
| #define setgcrefr(r, v) ((r).gcptr32 = (v).gcptr32) |
| |
| /* IMPORTANT NOTE: |
| ** |
| ** All uses of the setgcref* macros MUST be accompanied with a write barrier. |
| ** |
| ** This is to ensure the integrity of the incremental GC. The invariant |
| ** to preserve is that a black object never points to a white object. |
| ** I.e. never store a white object into a field of a black object. |
| ** |
| ** It's ok to LEAVE OUT the write barrier ONLY in the following cases: |
| ** - The source is not a GC object (NULL). |
| ** - The target is a GC root. I.e. everything in global_State. |
| ** - The target is a lua_State field (threads are never black). |
| ** - The target is a stack slot, see setgcV et al. |
| ** - The target is an open upvalue, i.e. pointing to a stack slot. |
| ** - The target is a newly created object (i.e. marked white). But make |
| ** sure nothing invokes the GC inbetween. |
| ** - The target and the source are the same object (self-reference). |
| ** - The target already contains the object (e.g. moving elements around). |
| ** |
| ** The most common case is a store to a stack slot. All other cases where |
| ** a barrier has been omitted are annotated with a NOBARRIER comment. |
| ** |
| ** The same logic applies for stores to table slots (array part or hash |
| ** part). ALL uses of lj_tab_set* require a barrier for the stored value |
| ** *and* the stored key, based on the above rules. In practice this means |
| ** a barrier is needed if *either* of the key or value are a GC object. |
| ** |
| ** It's ok to LEAVE OUT the write barrier in the following special cases: |
| ** - The stored value is nil. The key doesn't matter because it's either |
| ** not resurrected or lj_tab_newkey() will take care of the key barrier. |
| ** - The key doesn't matter if the *previously* stored value is guaranteed |
| ** to be non-nil (because the key is kept alive in the table). |
| ** - The key doesn't matter if it's guaranteed not to be part of the table, |
| ** since lj_tab_newkey() takes care of the key barrier. This applies |
| ** trivially to new tables, but watch out for resurrected keys. Storing |
| ** a nil value leaves the key in the table! |
| ** |
| ** In case of doubt use lj_gc_anybarriert() as it's rather cheap. It's used |
| ** by the interpreter for all table stores. |
| ** |
| ** Note: In contrast to Lua's GC, LuaJIT's GC does *not* specially mark |
| ** dead keys in tables. The reference is left in, but it's guaranteed to |
| ** be never dereferenced as long as the value is nil. It's ok if the key is |
| ** freed or if any object subsequently gets the same address. |
| ** |
| ** Not destroying dead keys helps to keep key hash slots stable. This avoids |
| ** specialization back-off for HREFK when a value flips between nil and |
| ** non-nil and the GC gets in the way. It also allows safely hoisting |
| ** HREF/HREFK across GC steps. Dead keys are only removed if a table is |
| ** resized (i.e. by NEWREF) and xREF must not be CSEd across a resize. |
| ** |
| ** The trade-off is that a write barrier for tables must take the key into |
| ** account, too. Implicitly resurrecting the key by storing a non-nil value |
| ** may invalidate the incremental GC invariant. |
| */ |
| |
| /* -- Common type definitions --------------------------------------------- */ |
| |
| /* Types for handling bytecodes. Need this here, details in lj_bc.h. */ |
| typedef uint32_t BCIns; /* Bytecode instruction. */ |
| typedef uint32_t BCPos; /* Bytecode position. */ |
| typedef uint32_t BCReg; /* Bytecode register. */ |
| typedef int32_t BCLine; /* Bytecode line number. */ |
| |
| /* Internal assembler functions. Never call these directly from C. */ |
| typedef void (*ASMFunction)(void); |
| |
| /* Resizable string buffer. Need this here, details in lj_str.h. */ |
| typedef struct SBuf { |
| char *buf; /* String buffer base. */ |
| MSize n; /* String buffer length. */ |
| MSize sz; /* String buffer size. */ |
| } SBuf; |
| |
| /* -- Tags and values ----------------------------------------------------- */ |
| |
| /* Frame link. */ |
| typedef union { |
| int32_t ftsz; /* Frame type and size of previous frame. */ |
| MRef pcr; /* Overlaps PC for Lua frames. */ |
| } FrameLink; |
| |
| /* Tagged value. */ |
| typedef LJ_ALIGN(8) union TValue { |
| uint64_t u64; /* 64 bit pattern overlaps number. */ |
| lua_Number n; /* Number object overlaps split tag/value object. */ |
| struct { |
| LJ_ENDIAN_LOHI( |
| union { |
| GCRef gcr; /* GCobj reference (if any). */ |
| int32_t i; /* Integer value. */ |
| }; |
| , uint32_t it; /* Internal object tag. Must overlap MSW of number. */ |
| ) |
| }; |
| struct { |
| LJ_ENDIAN_LOHI( |
| GCRef func; /* Function for next frame (or dummy L). */ |
| , FrameLink tp; /* Link to previous frame. */ |
| ) |
| } fr; |
| struct { |
| LJ_ENDIAN_LOHI( |
| uint32_t lo; /* Lower 32 bits of number. */ |
| , uint32_t hi; /* Upper 32 bits of number. */ |
| ) |
| } u32; |
| } TValue; |
| |
| typedef const TValue cTValue; |
| |
| #define tvref(r) (mref(r, TValue)) |
| |
| /* More external and GCobj tags for internal objects. */ |
| #define LAST_TT LUA_TTHREAD |
| #define LUA_TPROTO (LAST_TT+1) |
| #define LUA_TCDATA (LAST_TT+2) |
| |
| /* Internal object tags. |
| ** |
| ** Internal tags overlap the MSW of a number object (must be a double). |
| ** Interpreted as a double these are special NaNs. The FPU only generates |
| ** one type of NaN (0xfff8_0000_0000_0000). So MSWs > 0xfff80000 are available |
| ** for use as internal tags. Small negative numbers are used to shorten the |
| ** encoding of type comparisons (reg/mem against sign-ext. 8 bit immediate). |
| ** |
| ** ---MSW---.---LSW--- |
| ** primitive types | itype | | |
| ** lightuserdata | itype | void * | (32 bit platforms) |
| ** lightuserdata |ffff| void * | (64 bit platforms, 47 bit pointers) |
| ** GC objects | itype | GCRef | |
| ** int (LJ_DUALNUM)| itype | int | |
| ** number -------double------ |
| ** |
| ** ORDER LJ_T |
| ** Primitive types nil/false/true must be first, lightuserdata next. |
| ** GC objects are at the end, table/userdata must be lowest. |
| ** Also check lj_ir.h for similar ordering constraints. |
| */ |
| #define LJ_TNIL (~0u) |
| #define LJ_TFALSE (~1u) |
| #define LJ_TTRUE (~2u) |
| #define LJ_TLIGHTUD (~3u) |
| #define LJ_TSTR (~4u) |
| #define LJ_TUPVAL (~5u) |
| #define LJ_TTHREAD (~6u) |
| #define LJ_TPROTO (~7u) |
| #define LJ_TFUNC (~8u) |
| #define LJ_TTRACE (~9u) |
| #define LJ_TCDATA (~10u) |
| #define LJ_TTAB (~11u) |
| #define LJ_TUDATA (~12u) |
| /* This is just the canonical number type used in some places. */ |
| #define LJ_TNUMX (~13u) |
| |
| /* Integers have itype == LJ_TISNUM doubles have itype < LJ_TISNUM */ |
| #if LJ_64 |
| #define LJ_TISNUM 0xfffeffffu |
| #else |
| #define LJ_TISNUM LJ_TNUMX |
| #endif |
| #define LJ_TISTRUECOND LJ_TFALSE |
| #define LJ_TISPRI LJ_TTRUE |
| #define LJ_TISGCV (LJ_TSTR+1) |
| #define LJ_TISTABUD LJ_TTAB |
| |
| /* -- String object ------------------------------------------------------- */ |
| |
| /* String object header. String payload follows. */ |
| typedef struct GCstr { |
| GCHeader; |
| uint8_t reserved; /* Used by lexer for fast lookup of reserved words. */ |
| uint8_t unused; |
| MSize hash; /* Hash of string. */ |
| MSize len; /* Size of string. */ |
| } GCstr; |
| |
| #define strref(r) (&gcref((r))->str) |
| #define strdata(s) ((const char *)((s)+1)) |
| #define strdatawr(s) ((char *)((s)+1)) |
| #define strVdata(o) strdata(strV(o)) |
| #define sizestring(s) (sizeof(struct GCstr)+(s)->len+1) |
| |
| /* -- Userdata object ----------------------------------------------------- */ |
| |
| /* Userdata object. Payload follows. */ |
| typedef struct GCudata { |
| GCHeader; |
| uint8_t udtype; /* Userdata type. */ |
| uint8_t unused2; |
| GCRef env; /* Should be at same offset in GCfunc. */ |
| MSize len; /* Size of payload. */ |
| GCRef metatable; /* Must be at same offset in GCtab. */ |
| uint32_t align1; /* To force 8 byte alignment of the payload. */ |
| } GCudata; |
| |
| /* Userdata types. */ |
| enum { |
| UDTYPE_USERDATA, /* Regular userdata. */ |
| UDTYPE_IO_FILE, /* I/O library FILE. */ |
| UDTYPE_FFI_CLIB, /* FFI C library namespace. */ |
| UDTYPE__MAX |
| }; |
| |
| #define uddata(u) ((void *)((u)+1)) |
| #define sizeudata(u) (sizeof(struct GCudata)+(u)->len) |
| |
| /* -- C data object ------------------------------------------------------- */ |
| |
| /* C data object. Payload follows. */ |
| typedef struct GCcdata { |
| GCHeader; |
| uint16_t ctypeid; /* C type ID. */ |
| } GCcdata; |
| |
| /* Prepended to variable-sized or realigned C data objects. */ |
| typedef struct GCcdataVar { |
| uint16_t offset; /* Offset to allocated memory (relative to GCcdata). */ |
| uint16_t extra; /* Extra space allocated (incl. GCcdata + GCcdatav). */ |
| MSize len; /* Size of payload. */ |
| } GCcdataVar; |
| |
| #define cdataptr(cd) ((void *)((cd)+1)) |
| #define cdataisv(cd) ((cd)->marked & 0x80) |
| #define cdatav(cd) ((GCcdataVar *)((char *)(cd) - sizeof(GCcdataVar))) |
| #define cdatavlen(cd) check_exp(cdataisv(cd), cdatav(cd)->len) |
| #define sizecdatav(cd) (cdatavlen(cd) + cdatav(cd)->extra) |
| #define memcdatav(cd) ((void *)((char *)(cd) - cdatav(cd)->offset)) |
| |
| /* -- Prototype object ---------------------------------------------------- */ |
| |
| #define SCALE_NUM_GCO ((int32_t)sizeof(lua_Number)/sizeof(GCRef)) |
| #define round_nkgc(n) (((n) + SCALE_NUM_GCO-1) & ~(SCALE_NUM_GCO-1)) |
| |
| typedef struct GCproto { |
| GCHeader; |
| uint8_t numparams; /* Number of parameters. */ |
| uint8_t framesize; /* Fixed frame size. */ |
| MSize sizebc; /* Number of bytecode instructions. */ |
| GCRef gclist; |
| MRef k; /* Split constant array (points to the middle). */ |
| MRef uv; /* Upvalue list. local slot|0x8000 or parent uv idx. */ |
| MSize sizekgc; /* Number of collectable constants. */ |
| MSize sizekn; /* Number of lua_Number constants. */ |
| MSize sizept; /* Total size including colocated arrays. */ |
| uint8_t sizeuv; /* Number of upvalues. */ |
| uint8_t flags; /* Miscellaneous flags (see below). */ |
| uint16_t trace; /* Anchor for chain of root traces. */ |
| /* ------ The following fields are for debugging/tracebacks only ------ */ |
| GCRef chunkname; /* Name of the chunk this function was defined in. */ |
| BCLine firstline; /* First line of the function definition. */ |
| BCLine numline; /* Number of lines for the function definition. */ |
| MRef lineinfo; /* Compressed map from bytecode ins. to source line. */ |
| MRef uvinfo; /* Upvalue names. */ |
| MRef varinfo; /* Names and compressed extents of local variables. */ |
| } GCproto; |
| |
| /* Flags for prototype. */ |
| #define PROTO_CHILD 0x01 /* Has child prototypes. */ |
| #define PROTO_VARARG 0x02 /* Vararg function. */ |
| #define PROTO_FFI 0x04 /* Uses BC_KCDATA for FFI datatypes. */ |
| #define PROTO_NOJIT 0x08 /* JIT disabled for this function. */ |
| #define PROTO_ILOOP 0x10 /* Patched bytecode with ILOOP etc. */ |
| /* Only used during parsing. */ |
| #define PROTO_HAS_RETURN 0x20 /* Already emitted a return. */ |
| #define PROTO_FIXUP_RETURN 0x40 /* Need to fixup emitted returns. */ |
| /* Top bits used for counting created closures. */ |
| #define PROTO_CLCOUNT 0x20 /* Base of saturating 3 bit counter. */ |
| #define PROTO_CLC_BITS 3 |
| #define PROTO_CLC_POLY (3*PROTO_CLCOUNT) /* Polymorphic threshold. */ |
| |
| #define PROTO_UV_LOCAL 0x8000 /* Upvalue for local slot. */ |
| #define PROTO_UV_IMMUTABLE 0x4000 /* Immutable upvalue. */ |
| |
| #define proto_kgc(pt, idx) \ |
| check_exp((uintptr_t)(intptr_t)(idx) >= (uintptr_t)-(intptr_t)(pt)->sizekgc, \ |
| gcref(mref((pt)->k, GCRef)[(idx)])) |
| #define proto_knumtv(pt, idx) \ |
| check_exp((uintptr_t)(idx) < (pt)->sizekn, &mref((pt)->k, TValue)[(idx)]) |
| #define proto_bc(pt) ((BCIns *)((char *)(pt) + sizeof(GCproto))) |
| #define proto_bcpos(pt, pc) ((BCPos)((pc) - proto_bc(pt))) |
| #define proto_uv(pt) (mref((pt)->uv, uint16_t)) |
| |
| #define proto_chunkname(pt) (strref((pt)->chunkname)) |
| #define proto_chunknamestr(pt) (strdata(proto_chunkname((pt)))) |
| #define proto_lineinfo(pt) (mref((pt)->lineinfo, const void)) |
| #define proto_uvinfo(pt) (mref((pt)->uvinfo, const uint8_t)) |
| #define proto_varinfo(pt) (mref((pt)->varinfo, const uint8_t)) |
| |
| /* -- Upvalue object ------------------------------------------------------ */ |
| |
| typedef struct GCupval { |
| GCHeader; |
| uint8_t closed; /* Set if closed (i.e. uv->v == &uv->u.value). */ |
| uint8_t immutable; /* Immutable value. */ |
| union { |
| TValue tv; /* If closed: the value itself. */ |
| struct { /* If open: double linked list, anchored at thread. */ |
| GCRef prev; |
| GCRef next; |
| }; |
| }; |
| MRef v; /* Points to stack slot (open) or above (closed). */ |
| uint32_t dhash; /* Disambiguation hash: dh1 != dh2 => cannot alias. */ |
| } GCupval; |
| |
| #define uvprev(uv_) (&gcref((uv_)->prev)->uv) |
| #define uvnext(uv_) (&gcref((uv_)->next)->uv) |
| #define uvval(uv_) (mref((uv_)->v, TValue)) |
| |
| /* -- Function object (closures) ------------------------------------------ */ |
| |
| /* Common header for functions. env should be at same offset in GCudata. */ |
| #define GCfuncHeader \ |
| GCHeader; uint8_t ffid; uint8_t nupvalues; \ |
| GCRef env; GCRef gclist; MRef pc |
| |
| typedef struct GCfuncC { |
| GCfuncHeader; |
| lua_CFunction f; /* C function to be called. */ |
| TValue upvalue[1]; /* Array of upvalues (TValue). */ |
| } GCfuncC; |
| |
| typedef struct GCfuncL { |
| GCfuncHeader; |
| GCRef uvptr[1]; /* Array of _pointers_ to upvalue objects (GCupval). */ |
| } GCfuncL; |
| |
| typedef union GCfunc { |
| GCfuncC c; |
| GCfuncL l; |
| } GCfunc; |
| |
| #define FF_LUA 0 |
| #define FF_C 1 |
| #define isluafunc(fn) ((fn)->c.ffid == FF_LUA) |
| #define iscfunc(fn) ((fn)->c.ffid == FF_C) |
| #define isffunc(fn) ((fn)->c.ffid > FF_C) |
| #define funcproto(fn) \ |
| check_exp(isluafunc(fn), (GCproto *)(mref((fn)->l.pc, char)-sizeof(GCproto))) |
| #define sizeCfunc(n) (sizeof(GCfuncC)-sizeof(TValue)+sizeof(TValue)*(n)) |
| #define sizeLfunc(n) (sizeof(GCfuncL)-sizeof(GCRef)+sizeof(GCRef)*(n)) |
| |
| /* -- Table object -------------------------------------------------------- */ |
| |
| /* Hash node. */ |
| typedef struct Node { |
| TValue val; /* Value object. Must be first field. */ |
| TValue key; /* Key object. */ |
| MRef next; /* Hash chain. */ |
| MRef freetop; /* Top of free elements (stored in t->node[0]). */ |
| } Node; |
| |
| LJ_STATIC_ASSERT(offsetof(Node, val) == 0); |
| |
| typedef struct GCtab { |
| GCHeader; |
| uint8_t nomm; /* Negative cache for fast metamethods. */ |
| int8_t colo; /* Array colocation. */ |
| MRef array; /* Array part. */ |
| GCRef gclist; |
| GCRef metatable; /* Must be at same offset in GCudata. */ |
| MRef node; /* Hash part. */ |
| uint32_t asize; /* Size of array part (keys [0, asize-1]). */ |
| uint32_t hmask; /* Hash part mask (size of hash part - 1). */ |
| } GCtab; |
| |
| #define sizetabcolo(n) ((n)*sizeof(TValue) + sizeof(GCtab)) |
| #define tabref(r) (&gcref((r))->tab) |
| #define noderef(r) (mref((r), Node)) |
| #define nextnode(n) (mref((n)->next, Node)) |
| |
| /* -- State objects ------------------------------------------------------- */ |
| |
| /* VM states. */ |
| enum { |
| LJ_VMST_INTERP, /* Interpreter. */ |
| LJ_VMST_C, /* C function. */ |
| LJ_VMST_GC, /* Garbage collector. */ |
| LJ_VMST_EXIT, /* Trace exit handler. */ |
| LJ_VMST_RECORD, /* Trace recorder. */ |
| LJ_VMST_OPT, /* Optimizer. */ |
| LJ_VMST_ASM, /* Assembler. */ |
| LJ_VMST__MAX |
| }; |
| |
| #define setvmstate(g, st) ((g)->vmstate = ~LJ_VMST_##st) |
| |
| /* Metamethods. ORDER MM */ |
| #ifdef LJ_HASFFI |
| #define MMDEF_FFI(_) _(new) |
| #else |
| #define MMDEF_FFI(_) |
| #endif |
| |
| #if LJ_52 || LJ_HASFFI |
| #define MMDEF_PAIRS(_) _(pairs) _(ipairs) |
| #else |
| #define MMDEF_PAIRS(_) |
| #define MM_pairs 255 |
| #define MM_ipairs 255 |
| #endif |
| |
| #define MMDEF(_) \ |
| _(index) _(newindex) _(gc) _(mode) _(eq) _(len) \ |
| /* Only the above (fast) metamethods are negative cached (max. 8). */ \ |
| _(lt) _(le) _(concat) _(call) \ |
| /* The following must be in ORDER ARITH. */ \ |
| _(add) _(sub) _(mul) _(div) _(mod) _(pow) _(unm) \ |
| /* The following are used in the standard libraries. */ \ |
| _(metatable) _(tostring) MMDEF_FFI(_) MMDEF_PAIRS(_) |
| |
| typedef enum { |
| #define MMENUM(name) MM_##name, |
| MMDEF(MMENUM) |
| #undef MMENUM |
| MM__MAX, |
| MM____ = MM__MAX, |
| MM_FAST = MM_len |
| } MMS; |
| |
| /* GC root IDs. */ |
| typedef enum { |
| GCROOT_MMNAME, /* Metamethod names. */ |
| GCROOT_MMNAME_LAST = GCROOT_MMNAME + MM__MAX-1, |
| GCROOT_BASEMT, /* Metatables for base types. */ |
| GCROOT_BASEMT_NUM = GCROOT_BASEMT + ~LJ_TNUMX, |
| GCROOT_IO_INPUT, /* Userdata for default I/O input file. */ |
| GCROOT_IO_OUTPUT, /* Userdata for default I/O output file. */ |
| GCROOT_MAX |
| } GCRootID; |
| |
| #define basemt_it(g, it) ((g)->gcroot[GCROOT_BASEMT+~(it)]) |
| #define basemt_obj(g, o) ((g)->gcroot[GCROOT_BASEMT+itypemap(o)]) |
| #define mmname_str(g, mm) (strref((g)->gcroot[GCROOT_MMNAME+(mm)])) |
| |
| typedef struct GCState { |
| MSize total; /* Memory currently allocated. */ |
| MSize threshold; /* Memory threshold. */ |
| uint8_t currentwhite; /* Current white color. */ |
| uint8_t state; /* GC state. */ |
| uint8_t nocdatafin; /* No cdata finalizer called. */ |
| uint8_t unused2; |
| MSize sweepstr; /* Sweep position in string table. */ |
| GCRef root; /* List of all collectable objects. */ |
| MRef sweep; /* Sweep position in root list. */ |
| GCRef gray; /* List of gray objects. */ |
| GCRef grayagain; /* List of objects for atomic traversal. */ |
| GCRef weak; /* List of weak tables (to be cleared). */ |
| GCRef mmudata; /* List of userdata (to be finalized). */ |
| MSize stepmul; /* Incremental GC step granularity. */ |
| MSize debt; /* Debt (how much GC is behind schedule). */ |
| MSize estimate; /* Estimate of memory actually in use. */ |
| MSize pause; /* Pause between successive GC cycles. */ |
| } GCState; |
| |
| /* Global state, shared by all threads of a Lua universe. */ |
| typedef struct global_State { |
| GCRef *strhash; /* String hash table (hash chain anchors). */ |
| MSize strmask; /* String hash mask (size of hash table - 1). */ |
| MSize strnum; /* Number of strings in hash table. */ |
| lua_Alloc allocf; /* Memory allocator. */ |
| void *allocd; /* Memory allocator data. */ |
| GCState gc; /* Garbage collector. */ |
| SBuf tmpbuf; /* Temporary buffer for string concatenation. */ |
| Node nilnode; /* Fallback 1-element hash part (nil key and value). */ |
| GCstr strempty; /* Empty string. */ |
| uint8_t stremptyz; /* Zero terminator of empty string. */ |
| uint8_t hookmask; /* Hook mask. */ |
| uint8_t dispatchmode; /* Dispatch mode. */ |
| uint8_t vmevmask; /* VM event mask. */ |
| GCRef mainthref; /* Link to main thread. */ |
| TValue registrytv; /* Anchor for registry. */ |
| TValue tmptv, tmptv2; /* Temporary TValues. */ |
| GCupval uvhead; /* Head of double-linked list of all open upvalues. */ |
| int32_t hookcount; /* Instruction hook countdown. */ |
| int32_t hookcstart; /* Start count for instruction hook counter. */ |
| lua_Hook hookf; /* Hook function. */ |
| lua_CFunction wrapf; /* Wrapper for C function calls. */ |
| lua_CFunction panic; /* Called as a last resort for errors. */ |
| volatile int32_t vmstate; /* VM state or current JIT code trace number. */ |
| BCIns bc_cfunc_int; /* Bytecode for internal C function calls. */ |
| BCIns bc_cfunc_ext; /* Bytecode for external C function calls. */ |
| GCRef jit_L; /* Current JIT code lua_State or NULL. */ |
| MRef jit_base; /* Current JIT code L->base. */ |
| MRef ctype_state; /* Pointer to C type state. */ |
| GCRef gcroot[GCROOT_MAX]; /* GC roots. */ |
| } global_State; |
| |
| #define mainthread(g) (&gcref(g->mainthref)->th) |
| #define niltv(L) \ |
| check_exp(tvisnil(&G(L)->nilnode.val), &G(L)->nilnode.val) |
| #define niltvg(g) \ |
| check_exp(tvisnil(&(g)->nilnode.val), &(g)->nilnode.val) |
| |
| /* Hook management. Hook event masks are defined in lua.h. */ |
| #define HOOK_EVENTMASK 0x0f |
| #define HOOK_ACTIVE 0x10 |
| #define HOOK_ACTIVE_SHIFT 4 |
| #define HOOK_VMEVENT 0x20 |
| #define HOOK_GC 0x40 |
| #define hook_active(g) ((g)->hookmask & HOOK_ACTIVE) |
| #define hook_enter(g) ((g)->hookmask |= HOOK_ACTIVE) |
| #define hook_entergc(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_GC)) |
| #define hook_vmevent(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_VMEVENT)) |
| #define hook_leave(g) ((g)->hookmask &= ~HOOK_ACTIVE) |
| #define hook_save(g) ((g)->hookmask & ~HOOK_EVENTMASK) |
| #define hook_restore(g, h) \ |
| ((g)->hookmask = ((g)->hookmask & HOOK_EVENTMASK) | (h)) |
| |
| /* Per-thread state object. */ |
| struct lua_State { |
| GCHeader; |
| uint8_t dummy_ffid; /* Fake FF_C for curr_funcisL() on dummy frames. */ |
| uint8_t status; /* Thread status. */ |
| MRef glref; /* Link to global state. */ |
| GCRef gclist; /* GC chain. */ |
| TValue *base; /* Base of currently executing function. */ |
| TValue *top; /* First free slot in the stack. */ |
| MRef maxstack; /* Last free slot in the stack. */ |
| MRef stack; /* Stack base. */ |
| GCRef openupval; /* List of open upvalues in the stack. */ |
| GCRef env; /* Thread environment (table of globals). */ |
| void *cframe; /* End of C stack frame chain. */ |
| MSize stacksize; /* True stack size (incl. LJ_STACK_EXTRA). */ |
| }; |
| |
| #define G(L) (mref(L->glref, global_State)) |
| #define registry(L) (&G(L)->registrytv) |
| |
| /* Macros to access the currently executing (Lua) function. */ |
| #define curr_func(L) (&gcref((L->base-1)->fr.func)->fn) |
| #define curr_funcisL(L) (isluafunc(curr_func(L))) |
| #define curr_proto(L) (funcproto(curr_func(L))) |
| #define curr_topL(L) (L->base + curr_proto(L)->framesize) |
| #define curr_top(L) (curr_funcisL(L) ? curr_topL(L) : L->top) |
| |
| /* -- GC object definition and conversions -------------------------------- */ |
| |
| /* GC header for generic access to common fields of GC objects. */ |
| typedef struct GChead { |
| GCHeader; |
| uint8_t unused1; |
| uint8_t unused2; |
| GCRef env; |
| GCRef gclist; |
| GCRef metatable; |
| } GChead; |
| |
| /* The env field SHOULD be at the same offset for all GC objects. */ |
| LJ_STATIC_ASSERT(offsetof(GChead, env) == offsetof(GCfuncL, env)); |
| LJ_STATIC_ASSERT(offsetof(GChead, env) == offsetof(GCudata, env)); |
| |
| /* The metatable field MUST be at the same offset for all GC objects. */ |
| LJ_STATIC_ASSERT(offsetof(GChead, metatable) == offsetof(GCtab, metatable)); |
| LJ_STATIC_ASSERT(offsetof(GChead, metatable) == offsetof(GCudata, metatable)); |
| |
| /* The gclist field MUST be at the same offset for all GC objects. */ |
| LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(lua_State, gclist)); |
| LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCproto, gclist)); |
| LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCfuncL, gclist)); |
| LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCtab, gclist)); |
| |
| typedef union GCobj { |
| GChead gch; |
| GCstr str; |
| GCupval uv; |
| lua_State th; |
| GCproto pt; |
| GCfunc fn; |
| GCcdata cd; |
| GCtab tab; |
| GCudata ud; |
| } GCobj; |
| |
| /* Macros to convert a GCobj pointer into a specific value. */ |
| #define gco2str(o) check_exp((o)->gch.gct == ~LJ_TSTR, &(o)->str) |
| #define gco2uv(o) check_exp((o)->gch.gct == ~LJ_TUPVAL, &(o)->uv) |
| #define gco2th(o) check_exp((o)->gch.gct == ~LJ_TTHREAD, &(o)->th) |
| #define gco2pt(o) check_exp((o)->gch.gct == ~LJ_TPROTO, &(o)->pt) |
| #define gco2func(o) check_exp((o)->gch.gct == ~LJ_TFUNC, &(o)->fn) |
| #define gco2cd(o) check_exp((o)->gch.gct == ~LJ_TCDATA, &(o)->cd) |
| #define gco2tab(o) check_exp((o)->gch.gct == ~LJ_TTAB, &(o)->tab) |
| #define gco2ud(o) check_exp((o)->gch.gct == ~LJ_TUDATA, &(o)->ud) |
| |
| /* Macro to convert any collectable object into a GCobj pointer. */ |
| #define obj2gco(v) ((GCobj *)(v)) |
| |
| /* -- TValue getters/setters ---------------------------------------------- */ |
| |
| #ifdef LUA_USE_ASSERT |
| #include "lj_gc.h" |
| #endif |
| |
| /* Macros to test types. */ |
| #define itype(o) ((o)->it) |
| #define tvisnil(o) (itype(o) == LJ_TNIL) |
| #define tvisfalse(o) (itype(o) == LJ_TFALSE) |
| #define tvistrue(o) (itype(o) == LJ_TTRUE) |
| #define tvisbool(o) (tvisfalse(o) || tvistrue(o)) |
| #if LJ_64 |
| #define tvislightud(o) (((int32_t)itype(o) >> 15) == -2) |
| #else |
| #define tvislightud(o) (itype(o) == LJ_TLIGHTUD) |
| #endif |
| #define tvisstr(o) (itype(o) == LJ_TSTR) |
| #define tvisfunc(o) (itype(o) == LJ_TFUNC) |
| #define tvisthread(o) (itype(o) == LJ_TTHREAD) |
| #define tvisproto(o) (itype(o) == LJ_TPROTO) |
| #define tviscdata(o) (itype(o) == LJ_TCDATA) |
| #define tvistab(o) (itype(o) == LJ_TTAB) |
| #define tvisudata(o) (itype(o) == LJ_TUDATA) |
| #define tvisnumber(o) (itype(o) <= LJ_TISNUM) |
| #define tvisint(o) (LJ_DUALNUM && itype(o) == LJ_TISNUM) |
| #define tvisnum(o) (itype(o) < LJ_TISNUM) |
| |
| #define tvistruecond(o) (itype(o) < LJ_TISTRUECOND) |
| #define tvispri(o) (itype(o) >= LJ_TISPRI) |
| #define tvistabud(o) (itype(o) <= LJ_TISTABUD) /* && !tvisnum() */ |
| #define tvisgcv(o) ((itype(o) - LJ_TISGCV) > (LJ_TNUMX - LJ_TISGCV)) |
| |
| /* Special macros to test numbers for NaN, +0, -0, +1 and raw equality. */ |
| #define tvisnan(o) ((o)->n != (o)->n) |
| #if LJ_64 |
| #define tviszero(o) (((o)->u64 << 1) == 0) |
| #else |
| #define tviszero(o) (((o)->u32.lo | ((o)->u32.hi << 1)) == 0) |
| #endif |
| #define tvispzero(o) ((o)->u64 == 0) |
| #define tvismzero(o) ((o)->u64 == U64x(80000000,00000000)) |
| #define tvispone(o) ((o)->u64 == U64x(3ff00000,00000000)) |
| #define rawnumequal(o1, o2) ((o1)->u64 == (o2)->u64) |
| |
| /* Macros to convert type ids. */ |
| #if LJ_64 |
| #define itypemap(o) \ |
| (tvisnumber(o) ? ~LJ_TNUMX : tvislightud(o) ? ~LJ_TLIGHTUD : ~itype(o)) |
| #else |
| #define itypemap(o) (tvisnumber(o) ? ~LJ_TNUMX : ~itype(o)) |
| #endif |
| |
| /* Macros to get tagged values. */ |
| #define gcval(o) (gcref((o)->gcr)) |
| #define boolV(o) check_exp(tvisbool(o), (LJ_TFALSE - (o)->it)) |
| #if LJ_64 |
| #define lightudV(o) \ |
| check_exp(tvislightud(o), (void *)((o)->u64 & U64x(00007fff,ffffffff))) |
| #else |
| #define lightudV(o) check_exp(tvislightud(o), gcrefp((o)->gcr, void)) |
| #endif |
| #define gcV(o) check_exp(tvisgcv(o), gcval(o)) |
| #define strV(o) check_exp(tvisstr(o), &gcval(o)->str) |
| #define funcV(o) check_exp(tvisfunc(o), &gcval(o)->fn) |
| #define threadV(o) check_exp(tvisthread(o), &gcval(o)->th) |
| #define protoV(o) check_exp(tvisproto(o), &gcval(o)->pt) |
| #define cdataV(o) check_exp(tviscdata(o), &gcval(o)->cd) |
| #define tabV(o) check_exp(tvistab(o), &gcval(o)->tab) |
| #define udataV(o) check_exp(tvisudata(o), &gcval(o)->ud) |
| #define numV(o) check_exp(tvisnum(o), (o)->n) |
| #define intV(o) check_exp(tvisint(o), (int32_t)(o)->i) |
| |
| /* Macros to set tagged values. */ |
| #define setitype(o, i) ((o)->it = (i)) |
| #define setnilV(o) ((o)->it = LJ_TNIL) |
| #define setboolV(o, x) ((o)->it = LJ_TFALSE-(uint32_t)(x)) |
| |
| static LJ_AINLINE void setlightudV(TValue *o, void *p) |
| { |
| #if LJ_64 |
| o->u64 = (uint64_t)p | (((uint64_t)0xffff) << 48); |
| #else |
| setgcrefp(o->gcr, p); setitype(o, LJ_TLIGHTUD); |
| #endif |
| } |
| |
| #if LJ_64 |
| #define checklightudptr(L, p) \ |
| (((uint64_t)(p) >> 47) ? (lj_err_msg(L, LJ_ERR_BADLU), NULL) : (p)) |
| #define setcont(o, f) \ |
| ((o)->u64 = (uint64_t)(void *)(f) - (uint64_t)lj_vm_asm_begin) |
| #else |
| #define checklightudptr(L, p) (p) |
| #define setcont(o, f) setlightudV((o), (void *)(f)) |
| #endif |
| |
| #define tvchecklive(L, o) \ |
| UNUSED(L), lua_assert(!tvisgcv(o) || \ |
| ((~itype(o) == gcval(o)->gch.gct) && !isdead(G(L), gcval(o)))) |
| |
| static LJ_AINLINE void setgcV(lua_State *L, TValue *o, GCobj *v, uint32_t itype) |
| { |
| setgcref(o->gcr, v); setitype(o, itype); tvchecklive(L, o); |
| } |
| |
| #define define_setV(name, type, tag) \ |
| static LJ_AINLINE void name(lua_State *L, TValue *o, type *v) \ |
| { \ |
| setgcV(L, o, obj2gco(v), tag); \ |
| } |
| define_setV(setstrV, GCstr, LJ_TSTR) |
| define_setV(setthreadV, lua_State, LJ_TTHREAD) |
| define_setV(setprotoV, GCproto, LJ_TPROTO) |
| define_setV(setfuncV, GCfunc, LJ_TFUNC) |
| define_setV(setcdataV, GCcdata, LJ_TCDATA) |
| define_setV(settabV, GCtab, LJ_TTAB) |
| define_setV(setudataV, GCudata, LJ_TUDATA) |
| |
| #define setnumV(o, x) ((o)->n = (x)) |
| #define setnanV(o) ((o)->u64 = U64x(fff80000,00000000)) |
| #define setpinfV(o) ((o)->u64 = U64x(7ff00000,00000000)) |
| #define setminfV(o) ((o)->u64 = U64x(fff00000,00000000)) |
| |
| static LJ_AINLINE void setintV(TValue *o, int32_t i) |
| { |
| #if LJ_DUALNUM |
| o->i = (uint32_t)i; setitype(o, LJ_TISNUM); |
| #else |
| o->n = (lua_Number)i; |
| #endif |
| } |
| |
| static LJ_AINLINE void setint64V(TValue *o, int64_t i) |
| { |
| if (LJ_DUALNUM && LJ_LIKELY(i == (int64_t)(int32_t)i)) |
| setintV(o, (int32_t)i); |
| else |
| setnumV(o, (lua_Number)i); |
| } |
| |
| #if LJ_64 |
| #define setintptrV(o, i) setint64V((o), (i)) |
| #else |
| #define setintptrV(o, i) setintV((o), (i)) |
| #endif |
| |
| /* Copy tagged values. */ |
| static LJ_AINLINE void copyTV(lua_State *L, TValue *o1, const TValue *o2) |
| { |
| *o1 = *o2; tvchecklive(L, o1); |
| } |
| |
| /* -- Number to integer conversion ---------------------------------------- */ |
| |
| #if LJ_SOFTFP |
| LJ_ASMF int32_t lj_vm_tobit(double x); |
| #endif |
| |
| static LJ_AINLINE int32_t lj_num2bit(lua_Number n) |
| { |
| #if LJ_SOFTFP |
| return lj_vm_tobit(n); |
| #else |
| TValue o; |
| o.n = n + 6755399441055744.0; /* 2^52 + 2^51 */ |
| return (int32_t)o.u32.lo; |
| #endif |
| } |
| |
| #if LJ_TARGET_X86 && !defined(__SSE2__) |
| #define lj_num2int(n) lj_num2bit((n)) |
| #else |
| #define lj_num2int(n) ((int32_t)(n)) |
| #endif |
| |
| static LJ_AINLINE uint64_t lj_num2u64(lua_Number n) |
| { |
| #ifdef _MSC_VER |
| if (n >= 9223372036854775808.0) /* They think it's a feature. */ |
| return (uint64_t)(int64_t)(n - 18446744073709551616.0); |
| else |
| #endif |
| return (uint64_t)n; |
| } |
| |
| static LJ_AINLINE int32_t numberVint(cTValue *o) |
| { |
| if (LJ_LIKELY(tvisint(o))) |
| return intV(o); |
| else |
| return lj_num2int(numV(o)); |
| } |
| |
| static LJ_AINLINE lua_Number numberVnum(cTValue *o) |
| { |
| if (LJ_UNLIKELY(tvisint(o))) |
| return (lua_Number)intV(o); |
| else |
| return numV(o); |
| } |
| |
| /* -- Miscellaneous object handling --------------------------------------- */ |
| |
| /* Names and maps for internal and external object tags. */ |
| LJ_DATA const char *const lj_obj_typename[1+LUA_TCDATA+1]; |
| LJ_DATA const char *const lj_obj_itypename[~LJ_TNUMX+1]; |
| |
| #define lj_typename(o) (lj_obj_itypename[itypemap(o)]) |
| |
| /* Compare two objects without calling metamethods. */ |
| LJ_FUNC int lj_obj_equal(cTValue *o1, cTValue *o2); |
| |
| #endif |