Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • vlasami6/ni-run-template
  • chybijan/ni-run
  • ruzict16/FML
  • rozhovoj/ni-run-template
  • skrabmir/cfml
  • plodeada/ni-run-plodeada
  • stepam38/ni-run
  • hrncikar/ni-run
  • balikvo1/ni-run
9 results
Show changes
Commits on Source (10)
......@@ -2,8 +2,11 @@ image: $CI_REGISTRY/vlasami6/fmltest:master
 
test:
script:
- meson setup build
- meson setup build -D b_sanitize=address,undefined
- meson compile -C build
- cppcheck --error-exitcode=1 *.c
- env FML="$(readlink -f ./build/fml)" FML_REF_BC_INT=/cfml/fml /FMLtest/suite ast_interpret
hello_world.fml
- cppcheck --error-exitcode=1 src/*.c
- export ASAN_OPTIONS=symbolize=1:debug=1:detect_leaks=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
- export FML="$(readlink -f ./build/fml)"
- export FML_REF=/cfml/fml
- /FMLtest/suite ast_interpret
hello_world
/*
** DynASM encoding engine prototypes.
** Copyright (C) 2005-2022 Mike Pall. All rights reserved.
** Released under the MIT license. See dynasm.lua for full copyright notice.
*/
#ifndef _DASM_PROTO_H
#define _DASM_PROTO_H
#include <stddef.h>
#include <stdarg.h>
#define DASM_IDENT "DynASM 1.5.0"
#define DASM_VERSION 10500 /* 1.5.0 */
#ifndef Dst_DECL
#define Dst_DECL dasm_State **Dst
#endif
#ifndef Dst_REF
#define Dst_REF (*Dst)
#endif
#ifndef DASM_FDEF
#define DASM_FDEF extern
#endif
#ifndef DASM_M_GROW
#define DASM_M_GROW(ctx, t, p, sz, need) \
do { \
size_t _sz = (sz), _need = (need); \
if (_sz < _need) { \
if (_sz < 16) _sz = 16; \
while (_sz < _need) _sz += _sz; \
(p) = (t *)realloc((p), _sz); \
if ((p) == NULL) exit(1); \
(sz) = _sz; \
} \
} while(0)
#endif
#ifndef DASM_M_FREE
#define DASM_M_FREE(ctx, p, sz) free(p)
#endif
/* Internal DynASM encoder state. */
typedef struct dasm_State dasm_State;
/* Initialize and free DynASM state. */
DASM_FDEF void dasm_init(Dst_DECL, int maxsection);
DASM_FDEF void dasm_free(Dst_DECL);
/* Setup global array. Must be called before dasm_setup(). */
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
/* Grow PC label array. Can be called after dasm_setup(), too. */
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc);
/* Setup encoder. */
DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist);
/* Feed encoder with actions. Calls are generated by pre-processor. */
DASM_FDEF void dasm_put(Dst_DECL, int start, ...);
/* Link sections and return the resulting size. */
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp);
/* Encode sections into buffer. */
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer);
/* Get PC label offset. */
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc);
#ifdef DASM_CHECKS
/* Optional sanity checker to call between isolated encoding steps. */
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch);
#else
#define dasm_checkstep(a, b) 0
#endif
#endif /* _DASM_PROTO_H */
------------------------------------------------------------------------------
-- DynASM x64 module.
--
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved.
-- See dynasm.lua for full copyright notice.
------------------------------------------------------------------------------
-- This module just sets 64 bit mode for the combined x86/x64 module.
-- All the interesting stuff is there.
------------------------------------------------------------------------------
x64 = true -- Using a global is an ugly, but effective solution.
return require("dasm_x86")
/*
** DynASM x86 encoding engine.
** Copyright (C) 2005-2022 Mike Pall. All rights reserved.
** Released under the MIT license. See dynasm.lua for full copyright notice.
*/
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define DASM_ARCH "x86"
#ifndef DASM_EXTERN
#define DASM_EXTERN(a,b,c,d) 0
#endif
/* Action definitions. DASM_STOP must be 255. */
enum {
DASM_DISP = 233,
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB,
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC,
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN,
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP
};
/* Maximum number of section buffer positions for a single dasm_put() call. */
#define DASM_MAXSECPOS 25
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
#define DASM_S_OK 0x00000000
#define DASM_S_NOMEM 0x01000000
#define DASM_S_PHASE 0x02000000
#define DASM_S_MATCH_SEC 0x03000000
#define DASM_S_RANGE_I 0x11000000
#define DASM_S_RANGE_SEC 0x12000000
#define DASM_S_RANGE_LG 0x13000000
#define DASM_S_RANGE_PC 0x14000000
#define DASM_S_RANGE_VREG 0x15000000
#define DASM_S_UNDEF_L 0x21000000
#define DASM_S_UNDEF_PC 0x22000000
/* Macros to convert positions (8 bit section + 24 bit index). */
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
#define DASM_SEC2POS(sec) ((sec)<<24)
#define DASM_POS2SEC(pos) ((pos)>>24)
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
/* Action list type. */
typedef const unsigned char *dasm_ActList;
/* Per-section structure. */
typedef struct dasm_Section {
int *rbuf; /* Biased buffer pointer (negative section bias). */
int *buf; /* True buffer pointer. */
size_t bsize; /* Buffer size in bytes. */
int pos; /* Biased buffer position. */
int epos; /* End of biased buffer position - max single put. */
int ofs; /* Byte offset into section. */
} dasm_Section;
/* Core structure holding the DynASM encoding state. */
struct dasm_State {
size_t psize; /* Allocated size of this structure. */
dasm_ActList actionlist; /* Current actionlist pointer. */
int *lglabels; /* Local/global chain/pos ptrs. */
size_t lgsize;
int *pclabels; /* PC label chains/pos ptrs. */
size_t pcsize;
void **globals; /* Array of globals (bias -10). */
dasm_Section *section; /* Pointer to active section. */
size_t codesize; /* Total size of all code sections. */
int maxsection; /* 0 <= sectionidx < maxsection. */
int status; /* Status code. */
dasm_Section sections[1]; /* All sections. Alloc-extended. */
};
/* The size of the core structure depends on the max. number of sections. */
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
/* Initialize DynASM state. */
void dasm_init(Dst_DECL, int maxsection)
{
dasm_State *D;
size_t psz = 0;
int i;
Dst_REF = NULL;
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
D = Dst_REF;
D->psize = psz;
D->lglabels = NULL;
D->lgsize = 0;
D->pclabels = NULL;
D->pcsize = 0;
D->globals = NULL;
D->maxsection = maxsection;
for (i = 0; i < maxsection; i++) {
D->sections[i].buf = NULL; /* Need this for pass3. */
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
D->sections[i].bsize = 0;
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
}
}
/* Free DynASM state. */
void dasm_free(Dst_DECL)
{
dasm_State *D = Dst_REF;
int i;
for (i = 0; i < D->maxsection; i++)
if (D->sections[i].buf)
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
DASM_M_FREE(Dst, D, D->psize);
}
/* Setup global label array. Must be called before dasm_setup(). */
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
{
dasm_State *D = Dst_REF;
D->globals = gl - 10; /* Negative bias to compensate for locals. */
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
}
/* Grow PC label array. Can be called after dasm_setup(), too. */
void dasm_growpc(Dst_DECL, unsigned int maxpc)
{
dasm_State *D = Dst_REF;
size_t osz = D->pcsize;
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
}
/* Setup encoder. */
void dasm_setup(Dst_DECL, const void *actionlist)
{
dasm_State *D = Dst_REF;
int i;
D->actionlist = (dasm_ActList)actionlist;
D->status = DASM_S_OK;
D->section = &D->sections[0];
if (D->lgsize) memset((void *)D->lglabels, 0, D->lgsize);
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
for (i = 0; i < D->maxsection; i++) {
D->sections[i].pos = DASM_SEC2POS(i);
D->sections[i].ofs = 0;
}
}
#ifdef DASM_CHECKS
#define CK(x, st) \
do { if (!(x)) { \
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
#define CKPL(kind, st) \
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
D->status=DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
#else
#define CK(x, st) ((void)0)
#define CKPL(kind, st) ((void)0)
#endif
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
void dasm_put(Dst_DECL, int start, ...)
{
va_list ap;
dasm_State *D = Dst_REF;
dasm_ActList p = D->actionlist + start;
dasm_Section *sec = D->section;
int pos = sec->pos, ofs = sec->ofs, mrm = -1;
int *b;
if (pos >= sec->epos) {
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
}
b = sec->rbuf;
b[pos++] = start;
va_start(ap, start);
while (1) {
int action = *p++;
if (action < DASM_DISP) {
ofs++;
} else if (action <= DASM_REL_A) {
int n = va_arg(ap, int);
b[pos++] = n;
switch (action) {
case DASM_DISP:
if (n == 0) { if (mrm < 0) mrm = p[-2]; if ((mrm&7) != 5) break; }
/* fallthrough */
case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */
case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */
case DASM_IMM_D: ofs += 4; break;
case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob;
case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break;
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */
case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break;
case DASM_SPACE: p++; ofs += n; break;
case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */
case DASM_VREG: CK((n&-16) == 0 && (n != 4 || (*p>>5) != 2), RANGE_VREG);
if (*p < 0x40 && p[1] == DASM_DISP) mrm = n;
if (*p < 0x20 && (n&7) == 4) ofs++;
switch ((*p++ >> 3) & 3) {
case 3: n |= b[pos-3]; /* fallthrough */
case 2: n |= b[pos-2]; /* fallthrough */
case 1: if (n <= 7) { b[pos-1] |= 0x10; ofs--; }
}
continue;
}
mrm = -1;
} else {
int *pl, n;
switch (action) {
case DASM_REL_LG:
case DASM_IMM_LG:
n = *p++; pl = D->lglabels + n;
/* Bkwd rel or global. */
if (n <= 246) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
pl -= 246; n = *pl;
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
goto linkrel;
case DASM_REL_PC:
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
putrel:
n = *pl;
if (n < 0) { /* Label exists. Get label pos and store it. */
b[pos] = -n;
} else {
linkrel:
b[pos] = n; /* Else link to rel chain, anchored at label. */
*pl = pos;
}
pos++;
ofs += 4; /* Maximum offset needed. */
if (action == DASM_REL_LG || action == DASM_REL_PC) {
b[pos++] = ofs; /* Store pass1 offset estimate. */
} else if (sizeof(ptrdiff_t) == 8) {
ofs += 4;
}
break;
case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel;
case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
putlabel:
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; }
*pl = -pos; /* Label exists now. */
b[pos++] = ofs; /* Store pass1 offset estimate. */
break;
case DASM_ALIGN:
ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */
b[pos++] = ofs; /* Store pass1 offset estimate. */
break;
case DASM_EXTERN: p += 2; ofs += 4; break;
case DASM_ESC: p++; ofs++; break;
case DASM_MARK: mrm = p[-2]; break;
case DASM_SECTION:
n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n];
case DASM_STOP: goto stop;
}
}
}
stop:
va_end(ap);
sec->pos = pos;
sec->ofs = ofs;
}
#undef CK
/* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */
int dasm_link(Dst_DECL, size_t *szp)
{
dasm_State *D = Dst_REF;
int secnum;
int ofs = 0;
#ifdef DASM_CHECKS
*szp = 0;
if (D->status != DASM_S_OK) return D->status;
{
int pc;
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
}
#endif
{ /* Handle globals not defined in this translation unit. */
int idx;
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
int n = D->lglabels[idx];
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
}
}
/* Combine all code sections. No support for data sections (yet). */
for (secnum = 0; secnum < D->maxsection; secnum++) {
dasm_Section *sec = D->sections + secnum;
int *b = sec->rbuf;
int pos = DASM_SEC2POS(secnum);
int lastpos = sec->pos;
while (pos != lastpos) {
dasm_ActList p = D->actionlist + b[pos++];
int op = 0;
while (1) {
int action = *p++;
switch (action) {
case DASM_REL_LG: p++;
/* fallthrough */
case DASM_REL_PC: {
int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0);
if (shrink) { /* Shrinkable branch opcode? */
int lofs, lpos = b[pos];
if (lpos < 0) goto noshrink; /* Ext global? */
lofs = *DASM_POS2PTR(D, lpos);
if (lpos > pos) { /* Fwd label: add cumulative section offsets. */
int i;
for (i = secnum; i < DASM_POS2SEC(lpos); i++)
lofs += D->sections[i].ofs;
} else {
lofs -= ofs; /* Bkwd label: unfix offset. */
}
lofs -= b[pos+1]; /* Short branch ok? */
if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */
else { noshrink: shrink = 0; } /* No, cannot shrink op. */
}
b[pos+1] = shrink;
pos += 2;
break;
}
/* fallthrough */
case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++;
/* fallthrough */
case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W:
case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB:
case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break;
case DASM_LABEL_LG: p++;
/* fallthrough */
case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */
case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */
case DASM_EXTERN: p += 2; break;
case DASM_ESC: op = *p++; break;
case DASM_MARK: break;
case DASM_SECTION: case DASM_STOP: goto stop;
default: op = action; break;
}
}
stop: (void)0;
}
ofs += sec->ofs; /* Next section starts right after current section. */
}
D->codesize = ofs; /* Total size of all code sections */
*szp = ofs;
return DASM_S_OK;
}
#define dasmb(x) *cp++ = (unsigned char)(x)
#ifndef DASM_ALIGNED_WRITES
#define dasmw(x) \
do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
#define dasmd(x) \
do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
#define dasmq(x) \
do { *((unsigned long long *)cp) = (unsigned long long)(x); cp+=8; } while (0)
#else
#define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
#define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
#define dasmq(x) do { dasmd(x); dasmd((x)>>32); } while (0)
#endif
static unsigned char *dasma_(unsigned char *cp, ptrdiff_t x)
{
if (sizeof(ptrdiff_t) == 8)
dasmq((unsigned long long)x);
else
dasmd((unsigned int)x);
return cp;
}
#define dasma(x) (cp = dasma_(cp, (x)))
/* Pass 3: Encode sections. */
int dasm_encode(Dst_DECL, void *buffer)
{
dasm_State *D = Dst_REF;
unsigned char *base = (unsigned char *)buffer;
unsigned char *cp = base;
int secnum;
/* Encode all code sections. No support for data sections (yet). */
for (secnum = 0; secnum < D->maxsection; secnum++) {
dasm_Section *sec = D->sections + secnum;
int *b = sec->buf;
int *endb = sec->rbuf + sec->pos;
while (b != endb) {
dasm_ActList p = D->actionlist + *b++;
unsigned char *mark = NULL;
while (1) {
int action = *p++;
int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0;
switch (action) {
case DASM_DISP: if (!mark) mark = cp; {
unsigned char *mm = mark;
if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL;
if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7;
if (mrm != 5) { mm[-1] -= 0x80; break; } }
if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40;
}
/* fallthrough */
case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break;
case DASM_IMM_DB: if (((n+128)&-256) == 0) {
db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb;
} else mark = NULL;
/* fallthrough */
case DASM_IMM_D: wd: dasmd(n); break;
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL;
/* fallthrough */
case DASM_IMM_W: dasmw(n); break;
case DASM_VREG: {
int t = *p++;
unsigned char *ex = cp - (t&7);
if ((n & 8) && t < 0xa0) {
if (*ex & 0x80) ex[1] ^= 0x20 << (t>>6); else *ex ^= 1 << (t>>6);
n &= 7;
} else if (n & 0x10) {
if (*ex & 0x80) {
*ex = 0xc5; ex[1] = (ex[1] & 0x80) | ex[2]; ex += 2;
}
while (++ex < cp) ex[-1] = *ex;
if (mark) mark--;
cp--;
n &= 7;
}
if (t >= 0xc0) n <<= 4;
else if (t >= 0x40) n <<= 3;
else if (n == 4 && t < 0x20) { cp[-1] ^= n; *cp++ = 0x20; }
cp[-1] ^= n;
break;
}
case DASM_REL_LG: p++; if (n >= 0) goto rel_pc;
b++; n = (int)(ptrdiff_t)D->globals[-n];
/* fallthrough */
case DASM_REL_A: rel_a:
n -= (unsigned int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */
case DASM_REL_PC: rel_pc: {
int shrink = *b++;
int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; }
n = *pb - ((int)(cp-base) + 4-shrink);
if (shrink == 0) goto wd;
if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb;
goto wb;
}
case DASM_IMM_LG:
p++;
if (n < 0) { dasma((ptrdiff_t)D->globals[-n]); break; }
/* fallthrough */
case DASM_IMM_PC: {
int *pb = DASM_POS2PTR(D, n);
dasma(*pb < 0 ? (ptrdiff_t)pb[1] : (*pb + (ptrdiff_t)base));
break;
}
case DASM_LABEL_LG: {
int idx = *p++;
if (idx >= 10)
D->globals[idx] = (void *)(base + (*p == DASM_SETLABEL ? *b : n));
break;
}
case DASM_LABEL_PC: case DASM_SETLABEL: break;
case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; }
case DASM_ALIGN:
n = *p++;
while (((cp-base) & n)) *cp++ = 0x90; /* nop */
break;
case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd;
case DASM_MARK: mark = cp; break;
case DASM_ESC: action = *p++;
/* fallthrough */
default: *cp++ = action; break;
case DASM_SECTION: case DASM_STOP: goto stop;
}
}
stop: (void)0;
}
}
if (base + D->codesize != cp) /* Check for phase errors. */
return DASM_S_PHASE;
return DASM_S_OK;
}
/* Get PC label offset. */
int dasm_getpclabel(Dst_DECL, unsigned int pc)
{
dasm_State *D = Dst_REF;
if (pc*sizeof(int) < D->pcsize) {
int pos = D->pclabels[pc];
if (pos < 0) return *DASM_POS2PTR(D, -pos);
if (pos > 0) return -1; /* Undefined. */
}
return -2; /* Unused or out of range. */
}
#ifdef DASM_CHECKS
/* Optional sanity checker to call between isolated encoding steps. */
int dasm_checkstep(Dst_DECL, int secmatch)
{
dasm_State *D = Dst_REF;
if (D->status == DASM_S_OK) {
int i;
for (i = 1; i <= 9; i++) {
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_L|i; break; }
D->lglabels[i] = 0;
}
}
if (D->status == DASM_S_OK && secmatch >= 0 &&
D->section != &D->sections[secmatch])
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
return D->status;
}
#endif
------------------------------------------------------------------------------
-- DynASM x86/x64 module.
--
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved.
-- See dynasm.lua for full copyright notice.
------------------------------------------------------------------------------
local x64 = x64
-- Module information:
local _info = {
arch = x64 and "x64" or "x86",
description = "DynASM x86/x64 module",
version = "1.5.0",
vernum = 10500,
release = "2021-05-02",
author = "Mike Pall",
license = "MIT",
}
-- Exported glue functions for the arch-specific module.
local _M = { _info = _info }
-- Cache library functions.
local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs
local assert, unpack, setmetatable = assert, unpack or table.unpack, setmetatable
local _s = string
local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
local find, match, gmatch, gsub = _s.find, _s.match, _s.gmatch, _s.gsub
local concat, sort, remove = table.concat, table.sort, table.remove
local bit = bit or require("bit")
local band, bxor, shl, shr = bit.band, bit.bxor, bit.lshift, bit.rshift
-- Inherited tables and callbacks.
local g_opt, g_arch
local wline, werror, wfatal, wwarn
-- Action name list.
-- CHECK: Keep this in sync with the C code!
local action_names = {
-- int arg, 1 buffer pos:
"DISP", "IMM_S", "IMM_B", "IMM_W", "IMM_D", "IMM_WB", "IMM_DB",
-- action arg (1 byte), int arg, 1 buffer pos (reg/num):
"VREG", "SPACE",
-- ptrdiff_t arg, 1 buffer pos (address): !x64
"SETLABEL", "REL_A",
-- action arg (1 byte) or int arg, 2 buffer pos (link, offset):
"REL_LG", "REL_PC",
-- action arg (1 byte) or int arg, 1 buffer pos (link):
"IMM_LG", "IMM_PC",
-- action arg (1 byte) or int arg, 1 buffer pos (offset):
"LABEL_LG", "LABEL_PC",
-- action arg (1 byte), 1 buffer pos (offset):
"ALIGN",
-- action args (2 bytes), no buffer pos.
"EXTERN",
-- action arg (1 byte), no buffer pos.
"ESC",
-- no action arg, no buffer pos.
"MARK",
-- action arg (1 byte), no buffer pos, terminal action:
"SECTION",
-- no args, no buffer pos, terminal action:
"STOP"
}
-- Maximum number of section buffer positions for dasm_put().
-- CHECK: Keep this in sync with the C code!
local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines.
-- Action name -> action number (dynamically generated below).
local map_action = {}
-- First action number. Everything below does not need to be escaped.
local actfirst = 256-#action_names
-- Action list buffer and string (only used to remove dupes).
local actlist = {}
local actstr = ""
-- Argument list for next dasm_put(). Start with offset 0 into action list.
local actargs = { 0 }
-- Current number of section buffer positions for dasm_put().
local secpos = 1
-- VREG kind encodings, pre-shifted by 5 bits.
local map_vreg = {
["modrm.rm.m"] = 0x00,
["modrm.rm.r"] = 0x20,
["opcode"] = 0x20,
["sib.base"] = 0x20,
["sib.index"] = 0x40,
["modrm.reg"] = 0x80,
["vex.v"] = 0xa0,
["imm.hi"] = 0xc0,
}
-- Current number of VREG actions contributing to REX/VEX shrinkage.
local vreg_shrink_count = 0
------------------------------------------------------------------------------
-- Compute action numbers for action names.
for n,name in ipairs(action_names) do
local num = actfirst + n - 1
map_action[name] = num
end
-- Dump action names and numbers.
local function dumpactions(out)
out:write("DynASM encoding engine action codes:\n")
for n,name in ipairs(action_names) do
local num = map_action[name]
out:write(format(" %-10s %02X %d\n", name, num, num))
end
out:write("\n")
end
-- Write action list buffer as a huge static C array.
local function writeactions(out, name)
local nn = #actlist
local last = actlist[nn] or 255
actlist[nn] = nil -- Remove last byte.
if nn == 0 then nn = 1 end
out:write("static const unsigned char ", name, "[", nn, "] = {\n")
local s = " "
for n,b in ipairs(actlist) do
s = s..b..","
if #s >= 75 then
assert(out:write(s, "\n"))
s = " "
end
end
out:write(s, last, "\n};\n\n") -- Add last byte back.
end
------------------------------------------------------------------------------
-- Add byte to action list.
local function wputxb(n)
assert(n >= 0 and n <= 255 and n % 1 == 0, "byte out of range")
actlist[#actlist+1] = n
end
-- Add action to list with optional arg. Advance buffer pos, too.
local function waction(action, a, num)
wputxb(assert(map_action[action], "bad action name `"..action.."'"))
if a then actargs[#actargs+1] = a end
if a or num then secpos = secpos + (num or 1) end
end
-- Optionally add a VREG action.
local function wvreg(kind, vreg, psz, sk, defer)
if not vreg then return end
waction("VREG", vreg)
local b = assert(map_vreg[kind], "bad vreg kind `"..vreg.."'")
if b < (sk or 0) then
vreg_shrink_count = vreg_shrink_count + 1
end
if not defer then
b = b + vreg_shrink_count * 8
vreg_shrink_count = 0
end
wputxb(b + (psz or 0))
end
-- Add call to embedded DynASM C code.
local function wcall(func, args)
wline(format("dasm_%s(Dst, %s);", func, concat(args, ", ")), true)
end
-- Delete duplicate action list chunks. A tad slow, but so what.
local function dedupechunk(offset)
local al, as = actlist, actstr
local chunk = char(unpack(al, offset+1, #al))
local orig = find(as, chunk, 1, true)
if orig then
actargs[1] = orig-1 -- Replace with original offset.
for i=offset+1,#al do al[i] = nil end -- Kill dupe.
else
actstr = as..chunk
end
end
-- Flush action list (intervening C code or buffer pos overflow).
local function wflush(term)
local offset = actargs[1]
if #actlist == offset then return end -- Nothing to flush.
if not term then waction("STOP") end -- Terminate action list.
dedupechunk(offset)
wcall("put", actargs) -- Add call to dasm_put().
actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put().
secpos = 1 -- The actionlist offset occupies a buffer position, too.
end
-- Put escaped byte.
local function wputb(n)
if n >= actfirst then waction("ESC") end -- Need to escape byte.
wputxb(n)
end
------------------------------------------------------------------------------
-- Global label name -> global label number. With auto assignment on 1st use.
local next_global = 10
local map_global = setmetatable({}, { __index = function(t, name)
if not match(name, "^[%a_][%w_@]*$") then werror("bad global label") end
local n = next_global
if n > 246 then werror("too many global labels") end
next_global = n + 1
t[name] = n
return n
end})
-- Dump global labels.
local function dumpglobals(out, lvl)
local t = {}
for name, n in pairs(map_global) do t[n] = name end
out:write("Global labels:\n")
for i=10,next_global-1 do
out:write(format(" %s\n", t[i]))
end
out:write("\n")
end
-- Write global label enum.
local function writeglobals(out, prefix)
local t = {}
for name, n in pairs(map_global) do t[n] = name end
out:write("enum {\n")
for i=10,next_global-1 do
out:write(" ", prefix, gsub(t[i], "@.*", ""), ",\n")
end
out:write(" ", prefix, "_MAX\n};\n")
end
-- Write global label names.
local function writeglobalnames(out, name)
local t = {}
for name, n in pairs(map_global) do t[n] = name end
out:write("static const char *const ", name, "[] = {\n")
for i=10,next_global-1 do
out:write(" \"", t[i], "\",\n")
end
out:write(" (const char *)0\n};\n")
end
------------------------------------------------------------------------------
-- Extern label name -> extern label number. With auto assignment on 1st use.
local next_extern = -1
local map_extern = setmetatable({}, { __index = function(t, name)
-- No restrictions on the name for now.
local n = next_extern
if n < -256 then werror("too many extern labels") end
next_extern = n - 1
t[name] = n
return n
end})
-- Dump extern labels.
local function dumpexterns(out, lvl)
local t = {}
for name, n in pairs(map_extern) do t[-n] = name end
out:write("Extern labels:\n")
for i=1,-next_extern-1 do
out:write(format(" %s\n", t[i]))
end
out:write("\n")
end
-- Write extern label names.
local function writeexternnames(out, name)
local t = {}
for name, n in pairs(map_extern) do t[-n] = name end
out:write("static DASM_EXTERN_TYPE ", name, "[] = {\n")
for i=1,-next_extern-1 do
out:write(" DASM_EXTERN_NAME(", t[i], "),\n")
end
out:write(" DASM_EXTERN_LAST\n};\n")
end
------------------------------------------------------------------------------
-- Arch-specific maps.
local map_archdef = {} -- Ext. register name -> int. name.
local map_reg_rev = {} -- Int. register name -> ext. name.
local map_reg_num = {} -- Int. register name -> register number.
local map_reg_opsize = {} -- Int. register name -> operand size.
local map_reg_valid_base = {} -- Int. register name -> valid base register?
local map_reg_valid_index = {} -- Int. register name -> valid index register?
local map_reg_needrex = {} -- Int. register name -> need rex vs. no rex.
local reg_list = {} -- Canonical list of int. register names.
local map_type = {} -- Type name -> { ctype, reg }
local ctypenum = 0 -- Type number (for _PTx macros).
local addrsize = x64 and "q" or "d" -- Size for address operands.
-- Helper functions to fill register maps.
local function mkrmap(sz, cl, names)
local cname = format("@%s", sz)
reg_list[#reg_list+1] = cname
map_archdef[cl] = cname
map_reg_rev[cname] = cl
map_reg_num[cname] = -1
map_reg_opsize[cname] = sz
if sz == addrsize or sz == "d" then
map_reg_valid_base[cname] = true
map_reg_valid_index[cname] = true
end
if names then
for n,name in ipairs(names) do
local iname = format("@%s%x", sz, n-1)
reg_list[#reg_list+1] = iname
map_archdef[name] = iname
map_reg_rev[iname] = name
map_reg_num[iname] = n-1
map_reg_opsize[iname] = sz
if sz == "b" and n > 4 then map_reg_needrex[iname] = false end
if sz == addrsize or sz == "d" then
map_reg_valid_base[iname] = true
map_reg_valid_index[iname] = true
end
end
end
for i=0,(x64 and sz ~= "f") and 15 or 7 do
local needrex = sz == "b" and i > 3
local iname = format("@%s%x%s", sz, i, needrex and "R" or "")
if needrex then map_reg_needrex[iname] = true end
local name
if sz == "o" or sz == "y" then name = format("%s%d", cl, i)
elseif sz == "f" then name = format("st%d", i)
else name = format("r%d%s", i, sz == addrsize and "" or sz) end
map_archdef[name] = iname
if not map_reg_rev[iname] then
reg_list[#reg_list+1] = iname
map_reg_rev[iname] = name
map_reg_num[iname] = i
map_reg_opsize[iname] = sz
if sz == addrsize or sz == "d" then
map_reg_valid_base[iname] = true
map_reg_valid_index[iname] = true
end
end
end
reg_list[#reg_list+1] = ""
end
-- Integer registers (qword, dword, word and byte sized).
if x64 then
mkrmap("q", "Rq", {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"})
end
mkrmap("d", "Rd", {"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"})
mkrmap("w", "Rw", {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"})
mkrmap("b", "Rb", {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"})
map_reg_valid_index[map_archdef.esp] = false
if x64 then map_reg_valid_index[map_archdef.rsp] = false end
if x64 then map_reg_needrex[map_archdef.Rb] = true end
map_archdef["Ra"] = "@"..addrsize
-- FP registers (internally tword sized, but use "f" as operand size).
mkrmap("f", "Rf")
-- SSE registers (oword sized, but qword and dword accessible).
mkrmap("o", "xmm")
-- AVX registers (yword sized, but oword, qword and dword accessible).
mkrmap("y", "ymm")
-- Operand size prefixes to codes.
local map_opsize = {
byte = "b", word = "w", dword = "d", qword = "q", oword = "o", yword = "y",
tword = "t", aword = addrsize,
}
-- Operand size code to number.
local map_opsizenum = {
b = 1, w = 2, d = 4, q = 8, o = 16, y = 32, t = 10,
}
-- Operand size code to name.
local map_opsizename = {
b = "byte", w = "word", d = "dword", q = "qword", o = "oword", y = "yword",
t = "tword", f = "fpword",
}
-- Valid index register scale factors.
local map_xsc = {
["1"] = 0, ["2"] = 1, ["4"] = 2, ["8"] = 3,
}
-- Condition codes.
local map_cc = {
o = 0, no = 1, b = 2, nb = 3, e = 4, ne = 5, be = 6, nbe = 7,
s = 8, ns = 9, p = 10, np = 11, l = 12, nl = 13, le = 14, nle = 15,
c = 2, nae = 2, nc = 3, ae = 3, z = 4, nz = 5, na = 6, a = 7,
pe = 10, po = 11, nge = 12, ge = 13, ng = 14, g = 15,
}
-- Reverse defines for registers.
function _M.revdef(s)
return gsub(s, "@%w+", map_reg_rev)
end
-- Dump register names and numbers
local function dumpregs(out)
out:write("Register names, sizes and internal numbers:\n")
for _,reg in ipairs(reg_list) do
if reg == "" then
out:write("\n")
else
local name = map_reg_rev[reg]
local num = map_reg_num[reg]
local opsize = map_opsizename[map_reg_opsize[reg]]
out:write(format(" %-5s %-8s %s\n", name, opsize,
num < 0 and "(variable)" or num))
end
end
end
------------------------------------------------------------------------------
-- Put action for label arg (IMM_LG, IMM_PC, REL_LG, REL_PC).
local function wputlabel(aprefix, imm, num)
if type(imm) == "number" then
if imm < 0 then
waction("EXTERN")
wputxb(aprefix == "IMM_" and 0 or 1)
imm = -imm-1
else
waction(aprefix.."LG", nil, num);
end
wputxb(imm)
else
waction(aprefix.."PC", imm, num)
end
end
-- Put signed byte or arg.
local function wputsbarg(n)
if type(n) == "number" then
if n < -128 or n > 127 then
werror("signed immediate byte out of range")
end
if n < 0 then n = n + 256 end
wputb(n)
else waction("IMM_S", n) end
end
-- Put unsigned byte or arg.
local function wputbarg(n)
if type(n) == "number" then
if n < 0 or n > 255 then
werror("unsigned immediate byte out of range")
end
wputb(n)
else waction("IMM_B", n) end
end
-- Put unsigned word or arg.
local function wputwarg(n)
if type(n) == "number" then
if shr(n, 16) ~= 0 then
werror("unsigned immediate word out of range")
end
wputb(band(n, 255)); wputb(shr(n, 8));
else waction("IMM_W", n) end
end
-- Put signed or unsigned dword or arg.
local function wputdarg(n)
local tn = type(n)
if tn == "number" then
wputb(band(n, 255))
wputb(band(shr(n, 8), 255))
wputb(band(shr(n, 16), 255))
wputb(shr(n, 24))
elseif tn == "table" then
wputlabel("IMM_", n[1], 1)
else
waction("IMM_D", n)
end
end
-- Put signed or unsigned qword or arg.
local function wputqarg(n)
local tn = type(n)
if tn == "number" then -- This is only used for numbers from -2^31..2^32-1.
wputb(band(n, 255))
wputb(band(shr(n, 8), 255))
wputb(band(shr(n, 16), 255))
wputb(shr(n, 24))
local sign = n < 0 and 255 or 0
wputb(sign); wputb(sign); wputb(sign); wputb(sign)
else
waction("IMM_D", format("(unsigned int)(%s)", n))
waction("IMM_D", format("(unsigned int)((unsigned long long)(%s)>>32)", n))
end
end
-- Put operand-size dependent number or arg (defaults to dword).
local function wputszarg(sz, n)
if not sz or sz == "d" or sz == "q" then wputdarg(n)
elseif sz == "w" then wputwarg(n)
elseif sz == "b" then wputbarg(n)
elseif sz == "s" then wputsbarg(n)
else werror("bad operand size") end
end
-- Put multi-byte opcode with operand-size dependent modifications.
local function wputop(sz, op, rex, vex, vregr, vregxb)
local psz, sk = 0, nil
if vex then
local tail
if vex.m == 1 and band(rex, 11) == 0 then
if x64 and vregxb then
sk = map_vreg["modrm.reg"]
else
wputb(0xc5)
tail = shl(bxor(band(rex, 4), 4), 5)
psz = 3
end
end
if not tail then
wputb(0xc4)
wputb(shl(bxor(band(rex, 7), 7), 5) + vex.m)
tail = shl(band(rex, 8), 4)
psz = 4
end
local reg, vreg = 0, nil
if vex.v then
reg = vex.v.reg
if not reg then werror("bad vex operand") end
if reg < 0 then reg = 0; vreg = vex.v.vreg end
end
if sz == "y" or vex.l then tail = tail + 4 end
wputb(tail + shl(bxor(reg, 15), 3) + vex.p)
wvreg("vex.v", vreg)
rex = 0
if op >= 256 then werror("bad vex opcode") end
else
if rex ~= 0 then
if not x64 then werror("bad operand size") end
elseif (vregr or vregxb) and x64 then
rex = 0x10
sk = map_vreg["vex.v"]
end
end
local r
if sz == "w" then wputb(102) end
-- Needs >32 bit numbers, but only for crc32 eax, word [ebx]
if op >= 4294967296 then r = op%4294967296 wputb((op-r)/4294967296) op = r end
if op >= 16777216 then wputb(shr(op, 24)); op = band(op, 0xffffff) end
if op >= 65536 then
if rex ~= 0 then
local opc3 = band(op, 0xffff00)
if opc3 == 0x0f3a00 or opc3 == 0x0f3800 then
wputb(64 + band(rex, 15)); rex = 0; psz = 2
end
end
wputb(shr(op, 16)); op = band(op, 0xffff); psz = psz + 1
end
if op >= 256 then
local b = shr(op, 8)
if b == 15 and rex ~= 0 then wputb(64 + band(rex, 15)); rex = 0; psz = 2 end
wputb(b); op = band(op, 255); psz = psz + 1
end
if rex ~= 0 then wputb(64 + band(rex, 15)); psz = 2 end
if sz == "b" then op = op - 1 end
wputb(op)
return psz, sk
end
-- Put ModRM or SIB formatted byte.
local function wputmodrm(m, s, rm, vs, vrm)
assert(m < 4 and s < 16 and rm < 16, "bad modrm operands")
wputb(shl(m, 6) + shl(band(s, 7), 3) + band(rm, 7))
end
-- Put ModRM/SIB plus optional displacement.
local function wputmrmsib(t, imark, s, vsreg, psz, sk)
local vreg, vxreg
local reg, xreg = t.reg, t.xreg
if reg and reg < 0 then reg = 0; vreg = t.vreg end
if xreg and xreg < 0 then xreg = 0; vxreg = t.vxreg end
if s < 0 then s = 0 end
-- Register mode.
if sub(t.mode, 1, 1) == "r" then
wputmodrm(3, s, reg)
wvreg("modrm.reg", vsreg, psz+1, sk, vreg)
wvreg("modrm.rm.r", vreg, psz+1, sk)
return
end
local disp = t.disp
local tdisp = type(disp)
-- No base register?
if not reg then
local riprel = false
if xreg then
-- Indexed mode with index register only.
-- [xreg*xsc+disp] -> (0, s, esp) (xsc, xreg, ebp)
wputmodrm(0, s, 4)
if imark == "I" then waction("MARK") end
wvreg("modrm.reg", vsreg, psz+1, sk, vxreg)
wputmodrm(t.xsc, xreg, 5)
wvreg("sib.index", vxreg, psz+2, sk)
else
-- Pure 32 bit displacement.
if x64 and tdisp ~= "table" then
wputmodrm(0, s, 4) -- [disp] -> (0, s, esp) (0, esp, ebp)
wvreg("modrm.reg", vsreg, psz+1, sk)
if imark == "I" then waction("MARK") end
wputmodrm(0, 4, 5)
else
riprel = x64
wputmodrm(0, s, 5) -- [disp|rip-label] -> (0, s, ebp)
wvreg("modrm.reg", vsreg, psz+1, sk)
if imark == "I" then waction("MARK") end
end
end
if riprel then -- Emit rip-relative displacement.
if match("UWSiI", imark) then
werror("NYI: rip-relative displacement followed by immediate")
end
-- The previous byte in the action buffer cannot be 0xe9 or 0x80-0x8f.
wputlabel("REL_", disp[1], 2)
else
wputdarg(disp)
end
return
end
local m
if tdisp == "number" then -- Check displacement size at assembly time.
if disp == 0 and band(reg, 7) ~= 5 then -- [ebp] -> [ebp+0] (in SIB, too)
if not vreg then m = 0 end -- Force DISP to allow [Rd(5)] -> [ebp+0]
elseif disp >= -128 and disp <= 127 then m = 1
else m = 2 end
elseif tdisp == "table" then
m = 2
end
-- Index register present or esp as base register: need SIB encoding.
if xreg or band(reg, 7) == 4 then
wputmodrm(m or 2, s, 4) -- ModRM.
if m == nil or imark == "I" then waction("MARK") end
wvreg("modrm.reg", vsreg, psz+1, sk, vxreg or vreg)
wputmodrm(t.xsc or 0, xreg or 4, reg) -- SIB.
wvreg("sib.index", vxreg, psz+2, sk, vreg)
wvreg("sib.base", vreg, psz+2, sk)
else
wputmodrm(m or 2, s, reg) -- ModRM.
if (imark == "I" and (m == 1 or m == 2)) or
(m == nil and (vsreg or vreg)) then waction("MARK") end
wvreg("modrm.reg", vsreg, psz+1, sk, vreg)
wvreg("modrm.rm.m", vreg, psz+1, sk)
end
-- Put displacement.
if m == 1 then wputsbarg(disp)
elseif m == 2 then wputdarg(disp)
elseif m == nil then waction("DISP", disp) end
end
------------------------------------------------------------------------------
-- Return human-readable operand mode string.
local function opmodestr(op, args)
local m = {}
for i=1,#args do
local a = args[i]
m[#m+1] = sub(a.mode, 1, 1)..(a.opsize or "?")
end
return op.." "..concat(m, ",")
end
-- Convert number to valid integer or nil.
local function toint(expr, isqword)
local n = tonumber(expr)
if n then
if n % 1 ~= 0 then
werror("not an integer number `"..expr.."'")
elseif isqword then
if n < -2147483648 or n > 2147483647 then
n = nil -- Handle it as an expression to avoid precision loss.
end
elseif n < -2147483648 or n > 4294967295 then
werror("bad integer number `"..expr.."'")
end
return n
end
end
-- Parse immediate expression.
local function immexpr(expr)
-- &expr (pointer)
if sub(expr, 1, 1) == "&" then
return "iPJ", format("(ptrdiff_t)(%s)", sub(expr,2))
end
local prefix = sub(expr, 1, 2)
-- =>expr (pc label reference)
if prefix == "=>" then
return "iJ", sub(expr, 3)
end
-- ->name (global label reference)
if prefix == "->" then
return "iJ", map_global[sub(expr, 3)]
end
-- [<>][1-9] (local label reference)
local dir, lnum = match(expr, "^([<>])([1-9])$")
if dir then -- Fwd: 247-255, Bkwd: 1-9.
return "iJ", lnum + (dir == ">" and 246 or 0)
end
local extname = match(expr, "^extern%s+(%S+)$")
if extname then
return "iJ", map_extern[extname]
end
-- expr (interpreted as immediate)
return "iI", expr
end
-- Parse displacement expression: +-num, +-expr, +-opsize*num
local function dispexpr(expr)
local disp = expr == "" and 0 or toint(expr)
if disp then return disp end
local c, dispt = match(expr, "^([+-])%s*(.+)$")
if c == "+" then
expr = dispt
elseif not c then
werror("bad displacement expression `"..expr.."'")
end
local opsize, tailops = match(dispt, "^(%w+)%s*%*%s*(.+)$")
local ops, imm = map_opsize[opsize], toint(tailops)
if ops and imm then
if c == "-" then imm = -imm end
return imm*map_opsizenum[ops]
end
local mode, iexpr = immexpr(dispt)
if mode == "iJ" then
if c == "-" then werror("cannot invert label reference") end
return { iexpr }
end
return expr -- Need to return original signed expression.
end
-- Parse register or type expression.
local function rtexpr(expr)
if not expr then return end
local tname, ovreg = match(expr, "^([%w_]+):(@[%w_]+)$")
local tp = map_type[tname or expr]
if tp then
local reg = ovreg or tp.reg
local rnum = map_reg_num[reg]
if not rnum then
werror("type `"..(tname or expr).."' needs a register override")
end
if not map_reg_valid_base[reg] then
werror("bad base register override `"..(map_reg_rev[reg] or reg).."'")
end
return reg, rnum, tp
end
return expr, map_reg_num[expr]
end
-- Parse operand and return { mode, opsize, reg, xreg, xsc, disp, imm }.
local function parseoperand(param, isqword)
local t = {}
local expr = param
local opsize, tailops = match(param, "^(%w+)%s*(.+)$")
if opsize then
t.opsize = map_opsize[opsize]
if t.opsize then expr = tailops end
end
local br = match(expr, "^%[%s*(.-)%s*%]$")
repeat
if br then
t.mode = "xm"
-- [disp]
t.disp = toint(br)
if t.disp then
t.mode = x64 and "xm" or "xmO"
break
end
-- [reg...]
local tp
local reg, tailr = match(br, "^([@%w_:]+)%s*(.*)$")
reg, t.reg, tp = rtexpr(reg)
if not t.reg then
-- [expr]
t.mode = x64 and "xm" or "xmO"
t.disp = dispexpr("+"..br)
break
end
if t.reg == -1 then
t.vreg, tailr = match(tailr, "^(%b())(.*)$")
if not t.vreg then werror("bad variable register expression") end
end
-- [xreg*xsc] or [xreg*xsc+-disp] or [xreg*xsc+-expr]
local xsc, tailsc = match(tailr, "^%*%s*([1248])%s*(.*)$")
if xsc then
if not map_reg_valid_index[reg] then
werror("bad index register `"..map_reg_rev[reg].."'")
end
t.xsc = map_xsc[xsc]
t.xreg = t.reg
t.vxreg = t.vreg
t.reg = nil
t.vreg = nil
t.disp = dispexpr(tailsc)
break
end
if not map_reg_valid_base[reg] then
werror("bad base register `"..map_reg_rev[reg].."'")
end
-- [reg] or [reg+-disp]
t.disp = toint(tailr) or (tailr == "" and 0)
if t.disp then break end
-- [reg+xreg...]
local xreg, tailx = match(tailr, "^%+%s*([@%w_:]+)%s*(.*)$")
xreg, t.xreg, tp = rtexpr(xreg)
if not t.xreg then
-- [reg+-expr]
t.disp = dispexpr(tailr)
break
end
if not map_reg_valid_index[xreg] then
werror("bad index register `"..map_reg_rev[xreg].."'")
end
if t.xreg == -1 then
t.vxreg, tailx = match(tailx, "^(%b())(.*)$")
if not t.vxreg then werror("bad variable register expression") end
end
-- [reg+xreg*xsc...]
local xsc, tailsc = match(tailx, "^%*%s*([1248])%s*(.*)$")
if xsc then
t.xsc = map_xsc[xsc]
tailx = tailsc
end
-- [...] or [...+-disp] or [...+-expr]
t.disp = dispexpr(tailx)
else
-- imm or opsize*imm
local imm = toint(expr, isqword)
if not imm and sub(expr, 1, 1) == "*" and t.opsize then
imm = toint(sub(expr, 2))
if imm then
imm = imm * map_opsizenum[t.opsize]
t.opsize = nil
end
end
if imm then
if t.opsize then werror("bad operand size override") end
local m = "i"
if imm == 1 then m = m.."1" end
if imm >= 4294967168 and imm <= 4294967295 then imm = imm-4294967296 end
if imm >= -128 and imm <= 127 then m = m.."S" end
t.imm = imm
t.mode = m
break
end
local tp
local reg, tailr = match(expr, "^([@%w_:]+)%s*(.*)$")
reg, t.reg, tp = rtexpr(reg)
if t.reg then
if t.reg == -1 then
t.vreg, tailr = match(tailr, "^(%b())(.*)$")
if not t.vreg then werror("bad variable register expression") end
end
-- reg
if tailr == "" then
if t.opsize then werror("bad operand size override") end
t.opsize = map_reg_opsize[reg]
if t.opsize == "f" then
t.mode = t.reg == 0 and "fF" or "f"
else
if reg == "@w4" or (x64 and reg == "@d4") then
wwarn("bad idea, try again with `"..(x64 and "rsp'" or "esp'"))
end
t.mode = t.reg == 0 and "rmR" or (reg == "@b1" and "rmC" or "rm")
end
t.needrex = map_reg_needrex[reg]
break
end
-- type[idx], type[idx].field, type->field -> [reg+offset_expr]
if not tp then werror("bad operand `"..param.."'") end
t.mode = "xm"
t.disp = format(tp.ctypefmt, tailr)
else
t.mode, t.imm = immexpr(expr)
if sub(t.mode, -1) == "J" then
if t.opsize and t.opsize ~= addrsize then
werror("bad operand size override")
end
t.opsize = addrsize
end
end
end
until true
return t
end
------------------------------------------------------------------------------
-- x86 Template String Description
-- ===============================
--
-- Each template string is a list of [match:]pattern pairs,
-- separated by "|". The first match wins. No match means a
-- bad or unsupported combination of operand modes or sizes.
--
-- The match part and the ":" is omitted if the operation has
-- no operands. Otherwise the first N characters are matched
-- against the mode strings of each of the N operands.
--
-- The mode string for each operand type is (see parseoperand()):
-- Integer register: "rm", +"R" for eax, ax, al, +"C" for cl
-- FP register: "f", +"F" for st0
-- Index operand: "xm", +"O" for [disp] (pure offset)
-- Immediate: "i", +"S" for signed 8 bit, +"1" for 1,
-- +"I" for arg, +"P" for pointer
-- Any: +"J" for valid jump targets
--
-- So a match character "m" (mixed) matches both an integer register
-- and an index operand (to be encoded with the ModRM/SIB scheme).
-- But "r" matches only a register and "x" only an index operand
-- (e.g. for FP memory access operations).
--
-- The operand size match string starts right after the mode match
-- characters and ends before the ":". "dwb" or "qdwb" is assumed, if empty.
-- The effective data size of the operation is matched against this list.
--
-- If only the regular "b", "w", "d", "q", "t" operand sizes are
-- present, then all operands must be the same size. Unspecified sizes
-- are ignored, but at least one operand must have a size or the pattern
-- won't match (use the "byte", "word", "dword", "qword", "tword"
-- operand size overrides. E.g.: mov dword [eax], 1).
--
-- If the list has a "1" or "2" prefix, the operand size is taken
-- from the respective operand and any other operand sizes are ignored.
-- If the list contains only ".", all operand sizes are ignored.
-- If the list has a "/" prefix, the concatenated (mixed) operand sizes
-- are compared to the match.
--
-- E.g. "rrdw" matches for either two dword registers or two word
-- registers. "Fx2dq" matches an st0 operand plus an index operand
-- pointing to a dword (float) or qword (double).
--
-- Every character after the ":" is part of the pattern string:
-- Hex chars are accumulated to form the opcode (left to right).
-- "n" disables the standard opcode mods
-- (otherwise: -1 for "b", o16 prefix for "w", rex.w for "q")
-- "X" Force REX.W.
-- "r"/"R" adds the reg. number from the 1st/2nd operand to the opcode.
-- "m"/"M" generates ModRM/SIB from the 1st/2nd operand.
-- The spare 3 bits are either filled with the last hex digit or
-- the result from a previous "r"/"R". The opcode is restored.
-- "u" Use VEX encoding, vvvv unused.
-- "v"/"V" Use VEX encoding, vvvv from 1st/2nd operand (the operand is
-- removed from the list used by future characters).
-- "w" Use VEX encoding, vvvv from 3rd operand.
-- "L" Force VEX.L
--
-- All of the following characters force a flush of the opcode:
-- "o"/"O" stores a pure 32 bit disp (offset) from the 1st/2nd operand.
-- "s" stores a 4 bit immediate from the last register operand,
-- followed by 4 zero bits.
-- "S" stores a signed 8 bit immediate from the last operand.
-- "U" stores an unsigned 8 bit immediate from the last operand.
-- "W" stores an unsigned 16 bit immediate from the last operand.
-- "i" stores an operand sized immediate from the last operand.
-- "I" dito, but generates an action code to optionally modify
-- the opcode (+2) for a signed 8 bit immediate.
-- "J" generates one of the REL action codes from the last operand.
--
------------------------------------------------------------------------------
-- Template strings for x86 instructions. Ordered by first opcode byte.
-- Unimplemented opcodes (deliberate omissions) are marked with *.
local map_op = {
-- 00-05: add...
-- 06: *push es
-- 07: *pop es
-- 08-0D: or...
-- 0E: *push cs
-- 0F: two byte opcode prefix
-- 10-15: adc...
-- 16: *push ss
-- 17: *pop ss
-- 18-1D: sbb...
-- 1E: *push ds
-- 1F: *pop ds
-- 20-25: and...
es_0 = "26",
-- 27: *daa
-- 28-2D: sub...
cs_0 = "2E",
-- 2F: *das
-- 30-35: xor...
ss_0 = "36",
-- 37: *aaa
-- 38-3D: cmp...
ds_0 = "3E",
-- 3F: *aas
inc_1 = x64 and "m:FF0m" or "rdw:40r|m:FF0m",
dec_1 = x64 and "m:FF1m" or "rdw:48r|m:FF1m",
push_1 = (x64 and "rq:n50r|rw:50r|mq:nFF6m|mw:FF6m" or
"rdw:50r|mdw:FF6m").."|S.:6AS|ib:n6Ai|i.:68i",
pop_1 = x64 and "rq:n58r|rw:58r|mq:n8F0m|mw:8F0m" or "rdw:58r|mdw:8F0m",
-- 60: *pusha, *pushad, *pushaw
-- 61: *popa, *popad, *popaw
-- 62: *bound rdw,x
-- 63: x86: *arpl mw,rw
movsxd_2 = x64 and "rm/qd:63rM",
fs_0 = "64",
gs_0 = "65",
o16_0 = "66",
a16_0 = not x64 and "67" or nil,
a32_0 = x64 and "67",
-- 68: push idw
-- 69: imul rdw,mdw,idw
-- 6A: push ib
-- 6B: imul rdw,mdw,S
-- 6C: *insb
-- 6D: *insd, *insw
-- 6E: *outsb
-- 6F: *outsd, *outsw
-- 70-7F: jcc lb
-- 80: add... mb,i
-- 81: add... mdw,i
-- 82: *undefined
-- 83: add... mdw,S
test_2 = "mr:85Rm|rm:85rM|Ri:A9ri|mi:F70mi",
-- 86: xchg rb,mb
-- 87: xchg rdw,mdw
-- 88: mov mb,r
-- 89: mov mdw,r
-- 8A: mov r,mb
-- 8B: mov r,mdw
-- 8C: *mov mdw,seg
lea_2 = "rx1dq:8DrM",
-- 8E: *mov seg,mdw
-- 8F: pop mdw
nop_0 = "90",
xchg_2 = "Rrqdw:90R|rRqdw:90r|rm:87rM|mr:87Rm",
cbw_0 = "6698",
cwde_0 = "98",
cdqe_0 = "4898",
cwd_0 = "6699",
cdq_0 = "99",
cqo_0 = "4899",
-- 9A: *call iw:idw
wait_0 = "9B",
fwait_0 = "9B",
pushf_0 = "9C",
pushfd_0 = not x64 and "9C",
pushfq_0 = x64 and "9C",
popf_0 = "9D",
popfd_0 = not x64 and "9D",
popfq_0 = x64 and "9D",
sahf_0 = "9E",
lahf_0 = "9F",
mov_2 = "OR:A3o|RO:A1O|mr:89Rm|rm:8BrM|rib:nB0ri|ridw:B8ri|mi:C70mi",
movsb_0 = "A4",
movsw_0 = "66A5",
movsd_0 = "A5",
cmpsb_0 = "A6",
cmpsw_0 = "66A7",
cmpsd_0 = "A7",
-- A8: test Rb,i
-- A9: test Rdw,i
stosb_0 = "AA",
stosw_0 = "66AB",
stosd_0 = "AB",
lodsb_0 = "AC",
lodsw_0 = "66AD",
lodsd_0 = "AD",
scasb_0 = "AE",
scasw_0 = "66AF",
scasd_0 = "AF",
-- B0-B7: mov rb,i
-- B8-BF: mov rdw,i
-- C0: rol... mb,i
-- C1: rol... mdw,i
ret_1 = "i.:nC2W",
ret_0 = "C3",
-- C4: *les rdw,mq
-- C5: *lds rdw,mq
-- C6: mov mb,i
-- C7: mov mdw,i
-- C8: *enter iw,ib
leave_0 = "C9",
-- CA: *retf iw
-- CB: *retf
int3_0 = "CC",
int_1 = "i.:nCDU",
into_0 = "CE",
-- CF: *iret
-- D0: rol... mb,1
-- D1: rol... mdw,1
-- D2: rol... mb,cl
-- D3: rol... mb,cl
-- D4: *aam ib
-- D5: *aad ib
-- D6: *salc
-- D7: *xlat
-- D8-DF: floating point ops
-- E0: *loopne
-- E1: *loope
-- E2: *loop
-- E3: *jcxz, *jecxz
-- E4: *in Rb,ib
-- E5: *in Rdw,ib
-- E6: *out ib,Rb
-- E7: *out ib,Rdw
call_1 = x64 and "mq:nFF2m|J.:E8nJ" or "md:FF2m|J.:E8J",
jmp_1 = x64 and "mq:nFF4m|J.:E9nJ" or "md:FF4m|J.:E9J", -- short: EB
-- EA: *jmp iw:idw
-- EB: jmp ib
-- EC: *in Rb,dx
-- ED: *in Rdw,dx
-- EE: *out dx,Rb
-- EF: *out dx,Rdw
lock_0 = "F0",
int1_0 = "F1",
repne_0 = "F2",
repnz_0 = "F2",
rep_0 = "F3",
repe_0 = "F3",
repz_0 = "F3",
-- F4: *hlt
cmc_0 = "F5",
-- F6: test... mb,i; div... mb
-- F7: test... mdw,i; div... mdw
clc_0 = "F8",
stc_0 = "F9",
-- FA: *cli
cld_0 = "FC",
std_0 = "FD",
-- FE: inc... mb
-- FF: inc... mdw
-- misc ops
not_1 = "m:F72m",
neg_1 = "m:F73m",
mul_1 = "m:F74m",
imul_1 = "m:F75m",
div_1 = "m:F76m",
idiv_1 = "m:F77m",
imul_2 = "rmqdw:0FAFrM|rIqdw:69rmI|rSqdw:6BrmS|riqdw:69rmi",
imul_3 = "rmIqdw:69rMI|rmSqdw:6BrMS|rmiqdw:69rMi",
movzx_2 = "rm/db:0FB6rM|rm/qb:|rm/wb:0FB6rM|rm/dw:0FB7rM|rm/qw:",
movsx_2 = "rm/db:0FBErM|rm/qb:|rm/wb:0FBErM|rm/dw:0FBFrM|rm/qw:",
bswap_1 = "rqd:0FC8r",
bsf_2 = "rmqdw:0FBCrM",
bsr_2 = "rmqdw:0FBDrM",
bt_2 = "mrqdw:0FA3Rm|miqdw:0FBA4mU",
btc_2 = "mrqdw:0FBBRm|miqdw:0FBA7mU",
btr_2 = "mrqdw:0FB3Rm|miqdw:0FBA6mU",
bts_2 = "mrqdw:0FABRm|miqdw:0FBA5mU",
shld_3 = "mriqdw:0FA4RmU|mrC/qq:0FA5Rm|mrC/dd:|mrC/ww:",
shrd_3 = "mriqdw:0FACRmU|mrC/qq:0FADRm|mrC/dd:|mrC/ww:",
rdtsc_0 = "0F31", -- P1+
rdpmc_0 = "0F33", -- P6+
cpuid_0 = "0FA2", -- P1+
-- floating point ops
fst_1 = "ff:DDD0r|xd:D92m|xq:nDD2m",
fstp_1 = "ff:DDD8r|xd:D93m|xq:nDD3m|xt:DB7m",
fld_1 = "ff:D9C0r|xd:D90m|xq:nDD0m|xt:DB5m",
fpop_0 = "DDD8", -- Alias for fstp st0.
fist_1 = "xw:nDF2m|xd:DB2m",
fistp_1 = "xw:nDF3m|xd:DB3m|xq:nDF7m",
fild_1 = "xw:nDF0m|xd:DB0m|xq:nDF5m",
fxch_0 = "D9C9",
fxch_1 = "ff:D9C8r",
fxch_2 = "fFf:D9C8r|Fff:D9C8R",
fucom_1 = "ff:DDE0r",
fucom_2 = "Fff:DDE0R",
fucomp_1 = "ff:DDE8r",
fucomp_2 = "Fff:DDE8R",
fucomi_1 = "ff:DBE8r", -- P6+
fucomi_2 = "Fff:DBE8R", -- P6+
fucomip_1 = "ff:DFE8r", -- P6+
fucomip_2 = "Fff:DFE8R", -- P6+
fcomi_1 = "ff:DBF0r", -- P6+
fcomi_2 = "Fff:DBF0R", -- P6+
fcomip_1 = "ff:DFF0r", -- P6+
fcomip_2 = "Fff:DFF0R", -- P6+
fucompp_0 = "DAE9",
fcompp_0 = "DED9",
fldenv_1 = "x.:D94m",
fnstenv_1 = "x.:D96m",
fstenv_1 = "x.:9BD96m",
fldcw_1 = "xw:nD95m",
fstcw_1 = "xw:n9BD97m",
fnstcw_1 = "xw:nD97m",
fstsw_1 = "Rw:n9BDFE0|xw:n9BDD7m",
fnstsw_1 = "Rw:nDFE0|xw:nDD7m",
fclex_0 = "9BDBE2",
fnclex_0 = "DBE2",
fnop_0 = "D9D0",
-- D9D1-D9DF: unassigned
fchs_0 = "D9E0",
fabs_0 = "D9E1",
-- D9E2: unassigned
-- D9E3: unassigned
ftst_0 = "D9E4",
fxam_0 = "D9E5",
-- D9E6: unassigned
-- D9E7: unassigned
fld1_0 = "D9E8",
fldl2t_0 = "D9E9",
fldl2e_0 = "D9EA",
fldpi_0 = "D9EB",
fldlg2_0 = "D9EC",
fldln2_0 = "D9ED",
fldz_0 = "D9EE",
-- D9EF: unassigned
f2xm1_0 = "D9F0",
fyl2x_0 = "D9F1",
fptan_0 = "D9F2",
fpatan_0 = "D9F3",
fxtract_0 = "D9F4",
fprem1_0 = "D9F5",
fdecstp_0 = "D9F6",
fincstp_0 = "D9F7",
fprem_0 = "D9F8",
fyl2xp1_0 = "D9F9",
fsqrt_0 = "D9FA",
fsincos_0 = "D9FB",
frndint_0 = "D9FC",
fscale_0 = "D9FD",
fsin_0 = "D9FE",
fcos_0 = "D9FF",
-- SSE, SSE2
andnpd_2 = "rmo:660F55rM",
andnps_2 = "rmo:0F55rM",
andpd_2 = "rmo:660F54rM",
andps_2 = "rmo:0F54rM",
clflush_1 = "x.:0FAE7m",
cmppd_3 = "rmio:660FC2rMU",
cmpps_3 = "rmio:0FC2rMU",
cmpsd_3 = "rrio:F20FC2rMU|rxi/oq:",
cmpss_3 = "rrio:F30FC2rMU|rxi/od:",
comisd_2 = "rro:660F2FrM|rx/oq:",
comiss_2 = "rro:0F2FrM|rx/od:",
cvtdq2pd_2 = "rro:F30FE6rM|rx/oq:",
cvtdq2ps_2 = "rmo:0F5BrM",
cvtpd2dq_2 = "rmo:F20FE6rM",
cvtpd2ps_2 = "rmo:660F5ArM",
cvtpi2pd_2 = "rx/oq:660F2ArM",
cvtpi2ps_2 = "rx/oq:0F2ArM",
cvtps2dq_2 = "rmo:660F5BrM",
cvtps2pd_2 = "rro:0F5ArM|rx/oq:",
cvtsd2si_2 = "rr/do:F20F2DrM|rr/qo:|rx/dq:|rxq:",
cvtsd2ss_2 = "rro:F20F5ArM|rx/oq:",
cvtsi2sd_2 = "rm/od:F20F2ArM|rm/oq:F20F2ArXM",
cvtsi2ss_2 = "rm/od:F30F2ArM|rm/oq:F30F2ArXM",
cvtss2sd_2 = "rro:F30F5ArM|rx/od:",
cvtss2si_2 = "rr/do:F30F2DrM|rr/qo:|rxd:|rx/qd:",
cvttpd2dq_2 = "rmo:660FE6rM",
cvttps2dq_2 = "rmo:F30F5BrM",
cvttsd2si_2 = "rr/do:F20F2CrM|rr/qo:|rx/dq:|rxq:",
cvttss2si_2 = "rr/do:F30F2CrM|rr/qo:|rxd:|rx/qd:",
fxsave_1 = "x.:0FAE0m",
fxrstor_1 = "x.:0FAE1m",
ldmxcsr_1 = "xd:0FAE2m",
lfence_0 = "0FAEE8",
maskmovdqu_2 = "rro:660FF7rM",
mfence_0 = "0FAEF0",
movapd_2 = "rmo:660F28rM|mro:660F29Rm",
movaps_2 = "rmo:0F28rM|mro:0F29Rm",
movd_2 = "rm/od:660F6ErM|rm/oq:660F6ErXM|mr/do:660F7ERm|mr/qo:",
movdqa_2 = "rmo:660F6FrM|mro:660F7FRm",
movdqu_2 = "rmo:F30F6FrM|mro:F30F7FRm",
movhlps_2 = "rro:0F12rM",
movhpd_2 = "rx/oq:660F16rM|xr/qo:n660F17Rm",
movhps_2 = "rx/oq:0F16rM|xr/qo:n0F17Rm",
movlhps_2 = "rro:0F16rM",
movlpd_2 = "rx/oq:660F12rM|xr/qo:n660F13Rm",
movlps_2 = "rx/oq:0F12rM|xr/qo:n0F13Rm",
movmskpd_2 = "rr/do:660F50rM",
movmskps_2 = "rr/do:0F50rM",
movntdq_2 = "xro:660FE7Rm",
movnti_2 = "xrqd:0FC3Rm",
movntpd_2 = "xro:660F2BRm",
movntps_2 = "xro:0F2BRm",
movq_2 = "rro:F30F7ErM|rx/oq:|xr/qo:n660FD6Rm",
movsd_2 = "rro:F20F10rM|rx/oq:|xr/qo:nF20F11Rm",
movss_2 = "rro:F30F10rM|rx/od:|xr/do:F30F11Rm",
movupd_2 = "rmo:660F10rM|mro:660F11Rm",
movups_2 = "rmo:0F10rM|mro:0F11Rm",
orpd_2 = "rmo:660F56rM",
orps_2 = "rmo:0F56rM",
pause_0 = "F390",
pextrw_3 = "rri/do:660FC5rMU|xri/wo:660F3A15nRmU", -- Mem op: SSE4.1 only.
pinsrw_3 = "rri/od:660FC4rMU|rxi/ow:",
pmovmskb_2 = "rr/do:660FD7rM",
prefetchnta_1 = "xb:n0F180m",
prefetcht0_1 = "xb:n0F181m",
prefetcht1_1 = "xb:n0F182m",
prefetcht2_1 = "xb:n0F183m",
pshufd_3 = "rmio:660F70rMU",
pshufhw_3 = "rmio:F30F70rMU",
pshuflw_3 = "rmio:F20F70rMU",
pslld_2 = "rmo:660FF2rM|rio:660F726mU",
pslldq_2 = "rio:660F737mU",
psllq_2 = "rmo:660FF3rM|rio:660F736mU",
psllw_2 = "rmo:660FF1rM|rio:660F716mU",
psrad_2 = "rmo:660FE2rM|rio:660F724mU",
psraw_2 = "rmo:660FE1rM|rio:660F714mU",
psrld_2 = "rmo:660FD2rM|rio:660F722mU",
psrldq_2 = "rio:660F733mU",
psrlq_2 = "rmo:660FD3rM|rio:660F732mU",
psrlw_2 = "rmo:660FD1rM|rio:660F712mU",
rcpps_2 = "rmo:0F53rM",
rcpss_2 = "rro:F30F53rM|rx/od:",
rsqrtps_2 = "rmo:0F52rM",
rsqrtss_2 = "rmo:F30F52rM",
sfence_0 = "0FAEF8",
shufpd_3 = "rmio:660FC6rMU",
shufps_3 = "rmio:0FC6rMU",
stmxcsr_1 = "xd:0FAE3m",
ucomisd_2 = "rro:660F2ErM|rx/oq:",
ucomiss_2 = "rro:0F2ErM|rx/od:",
unpckhpd_2 = "rmo:660F15rM",
unpckhps_2 = "rmo:0F15rM",
unpcklpd_2 = "rmo:660F14rM",
unpcklps_2 = "rmo:0F14rM",
xorpd_2 = "rmo:660F57rM",
xorps_2 = "rmo:0F57rM",
-- SSE3 ops
fisttp_1 = "xw:nDF1m|xd:DB1m|xq:nDD1m",
addsubpd_2 = "rmo:660FD0rM",
addsubps_2 = "rmo:F20FD0rM",
haddpd_2 = "rmo:660F7CrM",
haddps_2 = "rmo:F20F7CrM",
hsubpd_2 = "rmo:660F7DrM",
hsubps_2 = "rmo:F20F7DrM",
lddqu_2 = "rxo:F20FF0rM",
movddup_2 = "rmo:F20F12rM",
movshdup_2 = "rmo:F30F16rM",
movsldup_2 = "rmo:F30F12rM",
-- SSSE3 ops
pabsb_2 = "rmo:660F381CrM",
pabsd_2 = "rmo:660F381ErM",
pabsw_2 = "rmo:660F381DrM",
palignr_3 = "rmio:660F3A0FrMU",
phaddd_2 = "rmo:660F3802rM",
phaddsw_2 = "rmo:660F3803rM",
phaddw_2 = "rmo:660F3801rM",
phsubd_2 = "rmo:660F3806rM",
phsubsw_2 = "rmo:660F3807rM",
phsubw_2 = "rmo:660F3805rM",
pmaddubsw_2 = "rmo:660F3804rM",
pmulhrsw_2 = "rmo:660F380BrM",
pshufb_2 = "rmo:660F3800rM",
psignb_2 = "rmo:660F3808rM",
psignd_2 = "rmo:660F380ArM",
psignw_2 = "rmo:660F3809rM",
-- SSE4.1 ops
blendpd_3 = "rmio:660F3A0DrMU",
blendps_3 = "rmio:660F3A0CrMU",
blendvpd_3 = "rmRo:660F3815rM",
blendvps_3 = "rmRo:660F3814rM",
dppd_3 = "rmio:660F3A41rMU",
dpps_3 = "rmio:660F3A40rMU",
extractps_3 = "mri/do:660F3A17RmU|rri/qo:660F3A17RXmU",
insertps_3 = "rrio:660F3A41rMU|rxi/od:",
movntdqa_2 = "rxo:660F382ArM",
mpsadbw_3 = "rmio:660F3A42rMU",
packusdw_2 = "rmo:660F382BrM",
pblendvb_3 = "rmRo:660F3810rM",
pblendw_3 = "rmio:660F3A0ErMU",
pcmpeqq_2 = "rmo:660F3829rM",
pextrb_3 = "rri/do:660F3A14nRmU|rri/qo:|xri/bo:",
pextrd_3 = "mri/do:660F3A16RmU",
pextrq_3 = "mri/qo:660F3A16RmU",
-- pextrw is SSE2, mem operand is SSE4.1 only
phminposuw_2 = "rmo:660F3841rM",
pinsrb_3 = "rri/od:660F3A20nrMU|rxi/ob:",
pinsrd_3 = "rmi/od:660F3A22rMU",
pinsrq_3 = "rmi/oq:660F3A22rXMU",
pmaxsb_2 = "rmo:660F383CrM",
pmaxsd_2 = "rmo:660F383DrM",
pmaxud_2 = "rmo:660F383FrM",
pmaxuw_2 = "rmo:660F383ErM",
pminsb_2 = "rmo:660F3838rM",
pminsd_2 = "rmo:660F3839rM",
pminud_2 = "rmo:660F383BrM",
pminuw_2 = "rmo:660F383ArM",
pmovsxbd_2 = "rro:660F3821rM|rx/od:",
pmovsxbq_2 = "rro:660F3822rM|rx/ow:",
pmovsxbw_2 = "rro:660F3820rM|rx/oq:",
pmovsxdq_2 = "rro:660F3825rM|rx/oq:",
pmovsxwd_2 = "rro:660F3823rM|rx/oq:",
pmovsxwq_2 = "rro:660F3824rM|rx/od:",
pmovzxbd_2 = "rro:660F3831rM|rx/od:",
pmovzxbq_2 = "rro:660F3832rM|rx/ow:",
pmovzxbw_2 = "rro:660F3830rM|rx/oq:",
pmovzxdq_2 = "rro:660F3835rM|rx/oq:",
pmovzxwd_2 = "rro:660F3833rM|rx/oq:",
pmovzxwq_2 = "rro:660F3834rM|rx/od:",
pmuldq_2 = "rmo:660F3828rM",
pmulld_2 = "rmo:660F3840rM",
ptest_2 = "rmo:660F3817rM",
roundpd_3 = "rmio:660F3A09rMU",
roundps_3 = "rmio:660F3A08rMU",
roundsd_3 = "rrio:660F3A0BrMU|rxi/oq:",
roundss_3 = "rrio:660F3A0ArMU|rxi/od:",
-- SSE4.2 ops
crc32_2 = "rmqd:F20F38F1rM|rm/dw:66F20F38F1rM|rm/db:F20F38F0rM|rm/qb:",
pcmpestri_3 = "rmio:660F3A61rMU",
pcmpestrm_3 = "rmio:660F3A60rMU",
pcmpgtq_2 = "rmo:660F3837rM",
pcmpistri_3 = "rmio:660F3A63rMU",
pcmpistrm_3 = "rmio:660F3A62rMU",
popcnt_2 = "rmqdw:F30FB8rM",
-- SSE4a
extrq_2 = "rro:660F79rM",
extrq_3 = "riio:660F780mUU",
insertq_2 = "rro:F20F79rM",
insertq_4 = "rriio:F20F78rMUU",
lzcnt_2 = "rmqdw:F30FBDrM",
movntsd_2 = "xr/qo:nF20F2BRm",
movntss_2 = "xr/do:F30F2BRm",
-- popcnt is also in SSE4.2
-- AES-NI
aesdec_2 = "rmo:660F38DErM",
aesdeclast_2 = "rmo:660F38DFrM",
aesenc_2 = "rmo:660F38DCrM",
aesenclast_2 = "rmo:660F38DDrM",
aesimc_2 = "rmo:660F38DBrM",
aeskeygenassist_3 = "rmio:660F3ADFrMU",
pclmulqdq_3 = "rmio:660F3A44rMU",
-- AVX FP ops
vaddsubpd_3 = "rrmoy:660FVD0rM",
vaddsubps_3 = "rrmoy:F20FVD0rM",
vandpd_3 = "rrmoy:660FV54rM",
vandps_3 = "rrmoy:0FV54rM",
vandnpd_3 = "rrmoy:660FV55rM",
vandnps_3 = "rrmoy:0FV55rM",
vblendpd_4 = "rrmioy:660F3AV0DrMU",
vblendps_4 = "rrmioy:660F3AV0CrMU",
vblendvpd_4 = "rrmroy:660F3AV4BrMs",
vblendvps_4 = "rrmroy:660F3AV4ArMs",
vbroadcastf128_2 = "rx/yo:660F38u1ArM",
vcmppd_4 = "rrmioy:660FVC2rMU",
vcmpps_4 = "rrmioy:0FVC2rMU",
vcmpsd_4 = "rrrio:F20FVC2rMU|rrxi/ooq:",
vcmpss_4 = "rrrio:F30FVC2rMU|rrxi/ood:",
vcomisd_2 = "rro:660Fu2FrM|rx/oq:",
vcomiss_2 = "rro:0Fu2FrM|rx/od:",
vcvtdq2pd_2 = "rro:F30FuE6rM|rx/oq:|rm/yo:",
vcvtdq2ps_2 = "rmoy:0Fu5BrM",
vcvtpd2dq_2 = "rmoy:F20FuE6rM",
vcvtpd2ps_2 = "rmoy:660Fu5ArM",
vcvtps2dq_2 = "rmoy:660Fu5BrM",
vcvtps2pd_2 = "rro:0Fu5ArM|rx/oq:|rm/yo:",
vcvtsd2si_2 = "rr/do:F20Fu2DrM|rx/dq:|rr/qo:|rxq:",
vcvtsd2ss_3 = "rrro:F20FV5ArM|rrx/ooq:",
vcvtsi2sd_3 = "rrm/ood:F20FV2ArM|rrm/ooq:F20FVX2ArM",
vcvtsi2ss_3 = "rrm/ood:F30FV2ArM|rrm/ooq:F30FVX2ArM",
vcvtss2sd_3 = "rrro:F30FV5ArM|rrx/ood:",
vcvtss2si_2 = "rr/do:F30Fu2DrM|rxd:|rr/qo:|rx/qd:",
vcvttpd2dq_2 = "rmo:660FuE6rM|rm/oy:660FuLE6rM",
vcvttps2dq_2 = "rmoy:F30Fu5BrM",
vcvttsd2si_2 = "rr/do:F20Fu2CrM|rx/dq:|rr/qo:|rxq:",
vcvttss2si_2 = "rr/do:F30Fu2CrM|rxd:|rr/qo:|rx/qd:",
vdppd_4 = "rrmio:660F3AV41rMU",
vdpps_4 = "rrmioy:660F3AV40rMU",
vextractf128_3 = "mri/oy:660F3AuL19RmU",
vextractps_3 = "mri/do:660F3Au17RmU",
vhaddpd_3 = "rrmoy:660FV7CrM",
vhaddps_3 = "rrmoy:F20FV7CrM",
vhsubpd_3 = "rrmoy:660FV7DrM",
vhsubps_3 = "rrmoy:F20FV7DrM",
vinsertf128_4 = "rrmi/yyo:660F3AV18rMU",
vinsertps_4 = "rrrio:660F3AV21rMU|rrxi/ood:",
vldmxcsr_1 = "xd:0FuAE2m",
vmaskmovps_3 = "rrxoy:660F38V2CrM|xrroy:660F38V2ERm",
vmaskmovpd_3 = "rrxoy:660F38V2DrM|xrroy:660F38V2FRm",
vmovapd_2 = "rmoy:660Fu28rM|mroy:660Fu29Rm",
vmovaps_2 = "rmoy:0Fu28rM|mroy:0Fu29Rm",
vmovd_2 = "rm/od:660Fu6ErM|rm/oq:660FuX6ErM|mr/do:660Fu7ERm|mr/qo:",
vmovq_2 = "rro:F30Fu7ErM|rx/oq:|xr/qo:660FuD6Rm",
vmovddup_2 = "rmy:F20Fu12rM|rro:|rx/oq:",
vmovhlps_3 = "rrro:0FV12rM",
vmovhpd_2 = "xr/qo:660Fu17Rm",
vmovhpd_3 = "rrx/ooq:660FV16rM",
vmovhps_2 = "xr/qo:0Fu17Rm",
vmovhps_3 = "rrx/ooq:0FV16rM",
vmovlhps_3 = "rrro:0FV16rM",
vmovlpd_2 = "xr/qo:660Fu13Rm",
vmovlpd_3 = "rrx/ooq:660FV12rM",
vmovlps_2 = "xr/qo:0Fu13Rm",
vmovlps_3 = "rrx/ooq:0FV12rM",
vmovmskpd_2 = "rr/do:660Fu50rM|rr/dy:660FuL50rM",
vmovmskps_2 = "rr/do:0Fu50rM|rr/dy:0FuL50rM",
vmovntpd_2 = "xroy:660Fu2BRm",
vmovntps_2 = "xroy:0Fu2BRm",
vmovsd_2 = "rx/oq:F20Fu10rM|xr/qo:F20Fu11Rm",
vmovsd_3 = "rrro:F20FV10rM",
vmovshdup_2 = "rmoy:F30Fu16rM",
vmovsldup_2 = "rmoy:F30Fu12rM",
vmovss_2 = "rx/od:F30Fu10rM|xr/do:F30Fu11Rm",
vmovss_3 = "rrro:F30FV10rM",
vmovupd_2 = "rmoy:660Fu10rM|mroy:660Fu11Rm",
vmovups_2 = "rmoy:0Fu10rM|mroy:0Fu11Rm",
vorpd_3 = "rrmoy:660FV56rM",
vorps_3 = "rrmoy:0FV56rM",
vpermilpd_3 = "rrmoy:660F38V0DrM|rmioy:660F3Au05rMU",
vpermilps_3 = "rrmoy:660F38V0CrM|rmioy:660F3Au04rMU",
vperm2f128_4 = "rrmiy:660F3AV06rMU",
vptestpd_2 = "rmoy:660F38u0FrM",
vptestps_2 = "rmoy:660F38u0ErM",
vrcpps_2 = "rmoy:0Fu53rM",
vrcpss_3 = "rrro:F30FV53rM|rrx/ood:",
vrsqrtps_2 = "rmoy:0Fu52rM",
vrsqrtss_3 = "rrro:F30FV52rM|rrx/ood:",
vroundpd_3 = "rmioy:660F3Au09rMU",
vroundps_3 = "rmioy:660F3Au08rMU",
vroundsd_4 = "rrrio:660F3AV0BrMU|rrxi/ooq:",
vroundss_4 = "rrrio:660F3AV0ArMU|rrxi/ood:",
vshufpd_4 = "rrmioy:660FVC6rMU",
vshufps_4 = "rrmioy:0FVC6rMU",
vsqrtps_2 = "rmoy:0Fu51rM",
vsqrtss_2 = "rro:F30Fu51rM|rx/od:",
vsqrtpd_2 = "rmoy:660Fu51rM",
vsqrtsd_2 = "rro:F20Fu51rM|rx/oq:",
vstmxcsr_1 = "xd:0FuAE3m",
vucomisd_2 = "rro:660Fu2ErM|rx/oq:",
vucomiss_2 = "rro:0Fu2ErM|rx/od:",
vunpckhpd_3 = "rrmoy:660FV15rM",
vunpckhps_3 = "rrmoy:0FV15rM",
vunpcklpd_3 = "rrmoy:660FV14rM",
vunpcklps_3 = "rrmoy:0FV14rM",
vxorpd_3 = "rrmoy:660FV57rM",
vxorps_3 = "rrmoy:0FV57rM",
vzeroall_0 = "0FuL77",
vzeroupper_0 = "0Fu77",
-- AVX2 FP ops
vbroadcastss_2 = "rx/od:660F38u18rM|rx/yd:|rro:|rr/yo:",
vbroadcastsd_2 = "rx/yq:660F38u19rM|rr/yo:",
-- *vgather* (!vsib)
vpermpd_3 = "rmiy:660F3AuX01rMU",
vpermps_3 = "rrmy:660F38V16rM",
-- AVX, AVX2 integer ops
-- In general, xmm requires AVX, ymm requires AVX2.
vaesdec_3 = "rrmo:660F38VDErM",
vaesdeclast_3 = "rrmo:660F38VDFrM",
vaesenc_3 = "rrmo:660F38VDCrM",
vaesenclast_3 = "rrmo:660F38VDDrM",
vaesimc_2 = "rmo:660F38uDBrM",
vaeskeygenassist_3 = "rmio:660F3AuDFrMU",
vlddqu_2 = "rxoy:F20FuF0rM",
vmaskmovdqu_2 = "rro:660FuF7rM",
vmovdqa_2 = "rmoy:660Fu6FrM|mroy:660Fu7FRm",
vmovdqu_2 = "rmoy:F30Fu6FrM|mroy:F30Fu7FRm",
vmovntdq_2 = "xroy:660FuE7Rm",
vmovntdqa_2 = "rxoy:660F38u2ArM",
vmpsadbw_4 = "rrmioy:660F3AV42rMU",
vpabsb_2 = "rmoy:660F38u1CrM",
vpabsd_2 = "rmoy:660F38u1ErM",
vpabsw_2 = "rmoy:660F38u1DrM",
vpackusdw_3 = "rrmoy:660F38V2BrM",
vpalignr_4 = "rrmioy:660F3AV0FrMU",
vpblendvb_4 = "rrmroy:660F3AV4CrMs",
vpblendw_4 = "rrmioy:660F3AV0ErMU",
vpclmulqdq_4 = "rrmio:660F3AV44rMU",
vpcmpeqq_3 = "rrmoy:660F38V29rM",
vpcmpestri_3 = "rmio:660F3Au61rMU",
vpcmpestrm_3 = "rmio:660F3Au60rMU",
vpcmpgtq_3 = "rrmoy:660F38V37rM",
vpcmpistri_3 = "rmio:660F3Au63rMU",
vpcmpistrm_3 = "rmio:660F3Au62rMU",
vpextrb_3 = "rri/do:660F3Au14nRmU|rri/qo:|xri/bo:",
vpextrw_3 = "rri/do:660FuC5rMU|xri/wo:660F3Au15nRmU",
vpextrd_3 = "mri/do:660F3Au16RmU",
vpextrq_3 = "mri/qo:660F3Au16RmU",
vphaddw_3 = "rrmoy:660F38V01rM",
vphaddd_3 = "rrmoy:660F38V02rM",
vphaddsw_3 = "rrmoy:660F38V03rM",
vphminposuw_2 = "rmo:660F38u41rM",
vphsubw_3 = "rrmoy:660F38V05rM",
vphsubd_3 = "rrmoy:660F38V06rM",
vphsubsw_3 = "rrmoy:660F38V07rM",
vpinsrb_4 = "rrri/ood:660F3AV20rMU|rrxi/oob:",
vpinsrw_4 = "rrri/ood:660FVC4rMU|rrxi/oow:",
vpinsrd_4 = "rrmi/ood:660F3AV22rMU",
vpinsrq_4 = "rrmi/ooq:660F3AVX22rMU",
vpmaddubsw_3 = "rrmoy:660F38V04rM",
vpmaxsb_3 = "rrmoy:660F38V3CrM",
vpmaxsd_3 = "rrmoy:660F38V3DrM",
vpmaxuw_3 = "rrmoy:660F38V3ErM",
vpmaxud_3 = "rrmoy:660F38V3FrM",
vpminsb_3 = "rrmoy:660F38V38rM",
vpminsd_3 = "rrmoy:660F38V39rM",
vpminuw_3 = "rrmoy:660F38V3ArM",
vpminud_3 = "rrmoy:660F38V3BrM",
vpmovmskb_2 = "rr/do:660FuD7rM|rr/dy:660FuLD7rM",
vpmovsxbw_2 = "rroy:660F38u20rM|rx/oq:|rx/yo:",
vpmovsxbd_2 = "rroy:660F38u21rM|rx/od:|rx/yq:",
vpmovsxbq_2 = "rroy:660F38u22rM|rx/ow:|rx/yd:",
vpmovsxwd_2 = "rroy:660F38u23rM|rx/oq:|rx/yo:",
vpmovsxwq_2 = "rroy:660F38u24rM|rx/od:|rx/yq:",
vpmovsxdq_2 = "rroy:660F38u25rM|rx/oq:|rx/yo:",
vpmovzxbw_2 = "rroy:660F38u30rM|rx/oq:|rx/yo:",
vpmovzxbd_2 = "rroy:660F38u31rM|rx/od:|rx/yq:",
vpmovzxbq_2 = "rroy:660F38u32rM|rx/ow:|rx/yd:",
vpmovzxwd_2 = "rroy:660F38u33rM|rx/oq:|rx/yo:",
vpmovzxwq_2 = "rroy:660F38u34rM|rx/od:|rx/yq:",
vpmovzxdq_2 = "rroy:660F38u35rM|rx/oq:|rx/yo:",
vpmuldq_3 = "rrmoy:660F38V28rM",
vpmulhrsw_3 = "rrmoy:660F38V0BrM",
vpmulld_3 = "rrmoy:660F38V40rM",
vpshufb_3 = "rrmoy:660F38V00rM",
vpshufd_3 = "rmioy:660Fu70rMU",
vpshufhw_3 = "rmioy:F30Fu70rMU",
vpshuflw_3 = "rmioy:F20Fu70rMU",
vpsignb_3 = "rrmoy:660F38V08rM",
vpsignw_3 = "rrmoy:660F38V09rM",
vpsignd_3 = "rrmoy:660F38V0ArM",
vpslldq_3 = "rrioy:660Fv737mU",
vpsllw_3 = "rrmoy:660FVF1rM|rrioy:660Fv716mU",
vpslld_3 = "rrmoy:660FVF2rM|rrioy:660Fv726mU",
vpsllq_3 = "rrmoy:660FVF3rM|rrioy:660Fv736mU",
vpsraw_3 = "rrmoy:660FVE1rM|rrioy:660Fv714mU",
vpsrad_3 = "rrmoy:660FVE2rM|rrioy:660Fv724mU",
vpsrldq_3 = "rrioy:660Fv733mU",
vpsrlw_3 = "rrmoy:660FVD1rM|rrioy:660Fv712mU",
vpsrld_3 = "rrmoy:660FVD2rM|rrioy:660Fv722mU",
vpsrlq_3 = "rrmoy:660FVD3rM|rrioy:660Fv732mU",
vptest_2 = "rmoy:660F38u17rM",
-- AVX2 integer ops
vbroadcasti128_2 = "rx/yo:660F38u5ArM",
vinserti128_4 = "rrmi/yyo:660F3AV38rMU",
vextracti128_3 = "mri/oy:660F3AuL39RmU",
vpblendd_4 = "rrmioy:660F3AV02rMU",
vpbroadcastb_2 = "rro:660F38u78rM|rx/ob:|rr/yo:|rx/yb:",
vpbroadcastw_2 = "rro:660F38u79rM|rx/ow:|rr/yo:|rx/yw:",
vpbroadcastd_2 = "rro:660F38u58rM|rx/od:|rr/yo:|rx/yd:",
vpbroadcastq_2 = "rro:660F38u59rM|rx/oq:|rr/yo:|rx/yq:",
vpermd_3 = "rrmy:660F38V36rM",
vpermq_3 = "rmiy:660F3AuX00rMU",
-- *vpgather* (!vsib)
vperm2i128_4 = "rrmiy:660F3AV46rMU",
vpmaskmovd_3 = "rrxoy:660F38V8CrM|xrroy:660F38V8ERm",
vpmaskmovq_3 = "rrxoy:660F38VX8CrM|xrroy:660F38VX8ERm",
vpsllvd_3 = "rrmoy:660F38V47rM",
vpsllvq_3 = "rrmoy:660F38VX47rM",
vpsravd_3 = "rrmoy:660F38V46rM",
vpsrlvd_3 = "rrmoy:660F38V45rM",
vpsrlvq_3 = "rrmoy:660F38VX45rM",
-- Intel ADX
adcx_2 = "rmqd:660F38F6rM",
adox_2 = "rmqd:F30F38F6rM",
-- BMI1
andn_3 = "rrmqd:0F38VF2rM",
bextr_3 = "rmrqd:0F38wF7rM",
blsi_2 = "rmqd:0F38vF33m",
blsmsk_2 = "rmqd:0F38vF32m",
blsr_2 = "rmqd:0F38vF31m",
tzcnt_2 = "rmqdw:F30FBCrM",
-- BMI2
bzhi_3 = "rmrqd:0F38wF5rM",
mulx_3 = "rrmqd:F20F38VF6rM",
pdep_3 = "rrmqd:F20F38VF5rM",
pext_3 = "rrmqd:F30F38VF5rM",
rorx_3 = "rmSqd:F20F3AuF0rMS",
sarx_3 = "rmrqd:F30F38wF7rM",
shrx_3 = "rmrqd:F20F38wF7rM",
shlx_3 = "rmrqd:660F38wF7rM",
-- FMA3
vfmaddsub132pd_3 = "rrmoy:660F38VX96rM",
vfmaddsub132ps_3 = "rrmoy:660F38V96rM",
vfmaddsub213pd_3 = "rrmoy:660F38VXA6rM",
vfmaddsub213ps_3 = "rrmoy:660F38VA6rM",
vfmaddsub231pd_3 = "rrmoy:660F38VXB6rM",
vfmaddsub231ps_3 = "rrmoy:660F38VB6rM",
vfmsubadd132pd_3 = "rrmoy:660F38VX97rM",
vfmsubadd132ps_3 = "rrmoy:660F38V97rM",
vfmsubadd213pd_3 = "rrmoy:660F38VXA7rM",
vfmsubadd213ps_3 = "rrmoy:660F38VA7rM",
vfmsubadd231pd_3 = "rrmoy:660F38VXB7rM",
vfmsubadd231ps_3 = "rrmoy:660F38VB7rM",
vfmadd132pd_3 = "rrmoy:660F38VX98rM",
vfmadd132ps_3 = "rrmoy:660F38V98rM",
vfmadd132sd_3 = "rrro:660F38VX99rM|rrx/ooq:",
vfmadd132ss_3 = "rrro:660F38V99rM|rrx/ood:",
vfmadd213pd_3 = "rrmoy:660F38VXA8rM",
vfmadd213ps_3 = "rrmoy:660F38VA8rM",
vfmadd213sd_3 = "rrro:660F38VXA9rM|rrx/ooq:",
vfmadd213ss_3 = "rrro:660F38VA9rM|rrx/ood:",
vfmadd231pd_3 = "rrmoy:660F38VXB8rM",
vfmadd231ps_3 = "rrmoy:660F38VB8rM",
vfmadd231sd_3 = "rrro:660F38VXB9rM|rrx/ooq:",
vfmadd231ss_3 = "rrro:660F38VB9rM|rrx/ood:",
vfmsub132pd_3 = "rrmoy:660F38VX9ArM",
vfmsub132ps_3 = "rrmoy:660F38V9ArM",
vfmsub132sd_3 = "rrro:660F38VX9BrM|rrx/ooq:",
vfmsub132ss_3 = "rrro:660F38V9BrM|rrx/ood:",
vfmsub213pd_3 = "rrmoy:660F38VXAArM",
vfmsub213ps_3 = "rrmoy:660F38VAArM",
vfmsub213sd_3 = "rrro:660F38VXABrM|rrx/ooq:",
vfmsub213ss_3 = "rrro:660F38VABrM|rrx/ood:",
vfmsub231pd_3 = "rrmoy:660F38VXBArM",
vfmsub231ps_3 = "rrmoy:660F38VBArM",
vfmsub231sd_3 = "rrro:660F38VXBBrM|rrx/ooq:",
vfmsub231ss_3 = "rrro:660F38VBBrM|rrx/ood:",
vfnmadd132pd_3 = "rrmoy:660F38VX9CrM",
vfnmadd132ps_3 = "rrmoy:660F38V9CrM",
vfnmadd132sd_3 = "rrro:660F38VX9DrM|rrx/ooq:",
vfnmadd132ss_3 = "rrro:660F38V9DrM|rrx/ood:",
vfnmadd213pd_3 = "rrmoy:660F38VXACrM",
vfnmadd213ps_3 = "rrmoy:660F38VACrM",
vfnmadd213sd_3 = "rrro:660F38VXADrM|rrx/ooq:",
vfnmadd213ss_3 = "rrro:660F38VADrM|rrx/ood:",
vfnmadd231pd_3 = "rrmoy:660F38VXBCrM",
vfnmadd231ps_3 = "rrmoy:660F38VBCrM",
vfnmadd231sd_3 = "rrro:660F38VXBDrM|rrx/ooq:",
vfnmadd231ss_3 = "rrro:660F38VBDrM|rrx/ood:",
vfnmsub132pd_3 = "rrmoy:660F38VX9ErM",
vfnmsub132ps_3 = "rrmoy:660F38V9ErM",
vfnmsub132sd_3 = "rrro:660F38VX9FrM|rrx/ooq:",
vfnmsub132ss_3 = "rrro:660F38V9FrM|rrx/ood:",
vfnmsub213pd_3 = "rrmoy:660F38VXAErM",
vfnmsub213ps_3 = "rrmoy:660F38VAErM",
vfnmsub213sd_3 = "rrro:660F38VXAFrM|rrx/ooq:",
vfnmsub213ss_3 = "rrro:660F38VAFrM|rrx/ood:",
vfnmsub231pd_3 = "rrmoy:660F38VXBErM",
vfnmsub231ps_3 = "rrmoy:660F38VBErM",
vfnmsub231sd_3 = "rrro:660F38VXBFrM|rrx/ooq:",
vfnmsub231ss_3 = "rrro:660F38VBFrM|rrx/ood:",
}
------------------------------------------------------------------------------
-- Arithmetic ops.
for name,n in pairs{ add = 0, ["or"] = 1, adc = 2, sbb = 3,
["and"] = 4, sub = 5, xor = 6, cmp = 7 } do
local n8 = shl(n, 3)
map_op[name.."_2"] = format(
"mr:%02XRm|rm:%02XrM|mI1qdw:81%XmI|mS1qdw:83%XmS|Ri1qdwb:%02Xri|mi1qdwb:81%Xmi",
1+n8, 3+n8, n, n, 5+n8, n)
end
-- Shift ops.
for name,n in pairs{ rol = 0, ror = 1, rcl = 2, rcr = 3,
shl = 4, shr = 5, sar = 7, sal = 4 } do
map_op[name.."_2"] = format("m1:D1%Xm|mC1qdwb:D3%Xm|mi:C1%XmU", n, n, n)
end
-- Conditional ops.
for cc,n in pairs(map_cc) do
map_op["j"..cc.."_1"] = format("J.:n0F8%XJ", n) -- short: 7%X
map_op["set"..cc.."_1"] = format("mb:n0F9%X2m", n)
map_op["cmov"..cc.."_2"] = format("rmqdw:0F4%XrM", n) -- P6+
end
-- FP arithmetic ops.
for name,n in pairs{ add = 0, mul = 1, com = 2, comp = 3,
sub = 4, subr = 5, div = 6, divr = 7 } do
local nc = 0xc0 + shl(n, 3)
local nr = nc + (n < 4 and 0 or (n % 2 == 0 and 8 or -8))
local fn = "f"..name
map_op[fn.."_1"] = format("ff:D8%02Xr|xd:D8%Xm|xq:nDC%Xm", nc, n, n)
if n == 2 or n == 3 then
map_op[fn.."_2"] = format("Fff:D8%02XR|Fx2d:D8%XM|Fx2q:nDC%XM", nc, n, n)
else
map_op[fn.."_2"] = format("Fff:D8%02XR|fFf:DC%02Xr|Fx2d:D8%XM|Fx2q:nDC%XM", nc, nr, n, n)
map_op[fn.."p_1"] = format("ff:DE%02Xr", nr)
map_op[fn.."p_2"] = format("fFf:DE%02Xr", nr)
end
map_op["fi"..name.."_1"] = format("xd:DA%Xm|xw:nDE%Xm", n, n)
end
-- FP conditional moves.
for cc,n in pairs{ b=0, e=1, be=2, u=3, nb=4, ne=5, nbe=6, nu=7 } do
local nc = 0xdac0 + shl(band(n, 3), 3) + shl(band(n, 4), 6)
map_op["fcmov"..cc.."_1"] = format("ff:%04Xr", nc) -- P6+
map_op["fcmov"..cc.."_2"] = format("Fff:%04XR", nc) -- P6+
end
-- SSE / AVX FP arithmetic ops.
for name,n in pairs{ sqrt = 1, add = 8, mul = 9,
sub = 12, min = 13, div = 14, max = 15 } do
map_op[name.."ps_2"] = format("rmo:0F5%XrM", n)
map_op[name.."ss_2"] = format("rro:F30F5%XrM|rx/od:", n)
map_op[name.."pd_2"] = format("rmo:660F5%XrM", n)
map_op[name.."sd_2"] = format("rro:F20F5%XrM|rx/oq:", n)
if n ~= 1 then
map_op["v"..name.."ps_3"] = format("rrmoy:0FV5%XrM", n)
map_op["v"..name.."ss_3"] = format("rrro:F30FV5%XrM|rrx/ood:", n)
map_op["v"..name.."pd_3"] = format("rrmoy:660FV5%XrM", n)
map_op["v"..name.."sd_3"] = format("rrro:F20FV5%XrM|rrx/ooq:", n)
end
end
-- SSE2 / AVX / AVX2 integer arithmetic ops (66 0F leaf).
for name,n in pairs{
paddb = 0xFC, paddw = 0xFD, paddd = 0xFE, paddq = 0xD4,
paddsb = 0xEC, paddsw = 0xED, packssdw = 0x6B,
packsswb = 0x63, packuswb = 0x67, paddusb = 0xDC,
paddusw = 0xDD, pand = 0xDB, pandn = 0xDF, pavgb = 0xE0,
pavgw = 0xE3, pcmpeqb = 0x74, pcmpeqd = 0x76,
pcmpeqw = 0x75, pcmpgtb = 0x64, pcmpgtd = 0x66,
pcmpgtw = 0x65, pmaddwd = 0xF5, pmaxsw = 0xEE,
pmaxub = 0xDE, pminsw = 0xEA, pminub = 0xDA,
pmulhuw = 0xE4, pmulhw = 0xE5, pmullw = 0xD5,
pmuludq = 0xF4, por = 0xEB, psadbw = 0xF6, psubb = 0xF8,
psubw = 0xF9, psubd = 0xFA, psubq = 0xFB, psubsb = 0xE8,
psubsw = 0xE9, psubusb = 0xD8, psubusw = 0xD9,
punpckhbw = 0x68, punpckhwd = 0x69, punpckhdq = 0x6A,
punpckhqdq = 0x6D, punpcklbw = 0x60, punpcklwd = 0x61,
punpckldq = 0x62, punpcklqdq = 0x6C, pxor = 0xEF
} do
map_op[name.."_2"] = format("rmo:660F%02XrM", n)
map_op["v"..name.."_3"] = format("rrmoy:660FV%02XrM", n)
end
------------------------------------------------------------------------------
local map_vexarg = { u = false, v = 1, V = 2, w = 3 }
-- Process pattern string.
local function dopattern(pat, args, sz, op, needrex)
local digit, addin, vex
local opcode = 0
local szov = sz
local narg = 1
local rex = 0
-- Limit number of section buffer positions used by a single dasm_put().
-- A single opcode needs a maximum of 6 positions.
if secpos+6 > maxsecpos then wflush() end
-- Process each character.
for c in gmatch(pat.."|", ".") do
if match(c, "%x") then -- Hex digit.
digit = byte(c) - 48
if digit > 48 then digit = digit - 39
elseif digit > 16 then digit = digit - 7 end
opcode = opcode*16 + digit
addin = nil
elseif c == "n" then -- Disable operand size mods for opcode.
szov = nil
elseif c == "X" then -- Force REX.W.
rex = 8
elseif c == "L" then -- Force VEX.L.
vex.l = true
elseif c == "r" then -- Merge 1st operand regno. into opcode.
addin = args[1]; opcode = opcode + (addin.reg % 8)
if narg < 2 then narg = 2 end
elseif c == "R" then -- Merge 2nd operand regno. into opcode.
addin = args[2]; opcode = opcode + (addin.reg % 8)
narg = 3
elseif c == "m" or c == "M" then -- Encode ModRM/SIB.
local s
if addin then
s = addin.reg
opcode = opcode - band(s, 7) -- Undo regno opcode merge.
else
s = band(opcode, 15) -- Undo last digit.
opcode = shr(opcode, 4)
end
local nn = c == "m" and 1 or 2
local t = args[nn]
if narg <= nn then narg = nn + 1 end
if szov == "q" and rex == 0 then rex = rex + 8 end
if t.reg and t.reg > 7 then rex = rex + 1 end
if t.xreg and t.xreg > 7 then rex = rex + 2 end
if s > 7 then rex = rex + 4 end
if needrex then rex = rex + 16 end
local psz, sk = wputop(szov, opcode, rex, vex, s < 0, t.vreg or t.vxreg)
opcode = nil
local imark = sub(pat, -1) -- Force a mark (ugly).
-- Put ModRM/SIB with regno/last digit as spare.
wputmrmsib(t, imark, s, addin and addin.vreg, psz, sk)
addin = nil
elseif map_vexarg[c] ~= nil then -- Encode using VEX prefix
local b = band(opcode, 255); opcode = shr(opcode, 8)
local m = 1
if b == 0x38 then m = 2
elseif b == 0x3a then m = 3 end
if m ~= 1 then b = band(opcode, 255); opcode = shr(opcode, 8) end
if b ~= 0x0f then
werror("expected `0F', `0F38', or `0F3A' to precede `"..c..
"' in pattern `"..pat.."' for `"..op.."'")
end
local v = map_vexarg[c]
if v then v = remove(args, v) end
b = band(opcode, 255)
local p = 0
if b == 0x66 then p = 1
elseif b == 0xf3 then p = 2
elseif b == 0xf2 then p = 3 end
if p ~= 0 then opcode = shr(opcode, 8) end
if opcode ~= 0 then wputop(nil, opcode, 0); opcode = 0 end
vex = { m = m, p = p, v = v }
else
if opcode then -- Flush opcode.
if szov == "q" and rex == 0 then rex = rex + 8 end
if needrex then rex = rex + 16 end
if addin and addin.reg == -1 then
local psz, sk = wputop(szov, opcode - 7, rex, vex, true)
wvreg("opcode", addin.vreg, psz, sk)
else
if addin and addin.reg > 7 then rex = rex + 1 end
wputop(szov, opcode, rex, vex)
end
opcode = nil
end
if c == "|" then break end
if c == "o" then -- Offset (pure 32 bit displacement).
wputdarg(args[1].disp); if narg < 2 then narg = 2 end
elseif c == "O" then
wputdarg(args[2].disp); narg = 3
else
-- Anything else is an immediate operand.
local a = args[narg]
narg = narg + 1
local mode, imm = a.mode, a.imm
if mode == "iJ" and not match(x64 and "J" or "iIJ", c) then
werror("bad operand size for label")
end
if c == "S" then
wputsbarg(imm)
elseif c == "U" then
wputbarg(imm)
elseif c == "W" then
wputwarg(imm)
elseif c == "i" or c == "I" then
if mode == "iJ" then
wputlabel("IMM_", imm, 1)
elseif mode == "iI" and c == "I" then
waction(sz == "w" and "IMM_WB" or "IMM_DB", imm)
else
wputszarg(sz, imm)
end
elseif c == "J" then
if mode == "iPJ" then
waction("REL_A", imm) -- !x64 (secpos)
else
wputlabel("REL_", imm, 2)
end
elseif c == "s" then
local reg = a.reg
if reg < 0 then
wputb(0)
wvreg("imm.hi", a.vreg)
else
wputb(shl(reg, 4))
end
else
werror("bad char `"..c.."' in pattern `"..pat.."' for `"..op.."'")
end
end
end
end
end
------------------------------------------------------------------------------
-- Mapping of operand modes to short names. Suppress output with '#'.
local map_modename = {
r = "reg", R = "eax", C = "cl", x = "mem", m = "mrm", i = "imm",
f = "stx", F = "st0", J = "lbl", ["1"] = "1",
I = "#", S = "#", O = "#",
}
-- Return a table/string showing all possible operand modes.
local function templatehelp(template, nparams)
if nparams == 0 then return "" end
local t = {}
for tm in gmatch(template, "[^%|]+") do
local s = map_modename[sub(tm, 1, 1)]
s = s..gsub(sub(tm, 2, nparams), ".", function(c)
return ", "..map_modename[c]
end)
if not match(s, "#") then t[#t+1] = s end
end
return t
end
-- Match operand modes against mode match part of template.
local function matchtm(tm, args)
for i=1,#args do
if not match(args[i].mode, sub(tm, i, i)) then return end
end
return true
end
-- Handle opcodes defined with template strings.
map_op[".template__"] = function(params, template, nparams)
if not params then return templatehelp(template, nparams) end
local args = {}
-- Zero-operand opcodes have no match part.
if #params == 0 then
dopattern(template, args, "d", params.op, nil)
return
end
-- Determine common operand size (coerce undefined size) or flag as mixed.
local sz, szmix, needrex
for i,p in ipairs(params) do
args[i] = parseoperand(p)
local nsz = args[i].opsize
if nsz then
if sz and sz ~= nsz then szmix = true else sz = nsz end
end
local nrex = args[i].needrex
if nrex ~= nil then
if needrex == nil then
needrex = nrex
elseif needrex ~= nrex then
werror("bad mix of byte-addressable registers")
end
end
end
-- Try all match:pattern pairs (separated by '|').
local gotmatch, lastpat
for tm in gmatch(template, "[^%|]+") do
-- Split off size match (starts after mode match) and pattern string.
local szm, pat = match(tm, "^(.-):(.*)$", #args+1)
if pat == "" then pat = lastpat else lastpat = pat end
if matchtm(tm, args) then
local prefix = sub(szm, 1, 1)
if prefix == "/" then -- Exactly match leading operand sizes.
for i = #szm,1,-1 do
if i == 1 then
dopattern(pat, args, sz, params.op, needrex) -- Process pattern.
return
elseif args[i-1].opsize ~= sub(szm, i, i) then
break
end
end
else -- Match common operand size.
local szp = sz
if szm == "" then szm = x64 and "qdwb" or "dwb" end -- Default sizes.
if prefix == "1" then szp = args[1].opsize; szmix = nil
elseif prefix == "2" then szp = args[2].opsize; szmix = nil end
if not szmix and (prefix == "." or match(szm, szp or "#")) then
dopattern(pat, args, szp, params.op, needrex) -- Process pattern.
return
end
end
gotmatch = true
end
end
local msg = "bad operand mode"
if gotmatch then
if szmix then
msg = "mixed operand size"
else
msg = sz and "bad operand size" or "missing operand size"
end
end
werror(msg.." in `"..opmodestr(params.op, args).."'")
end
------------------------------------------------------------------------------
-- x64-specific opcode for 64 bit immediates and displacements.
if x64 then
function map_op.mov64_2(params)
if not params then return { "reg, imm", "reg, [disp]", "[disp], reg" } end
if secpos+2 > maxsecpos then wflush() end
local opcode, op64, sz, rex, vreg
local op64 = match(params[1], "^%[%s*(.-)%s*%]$")
if op64 then
local a = parseoperand(params[2])
if a.mode ~= "rmR" then werror("bad operand mode") end
sz = a.opsize
rex = sz == "q" and 8 or 0
opcode = 0xa3
else
op64 = match(params[2], "^%[%s*(.-)%s*%]$")
local a = parseoperand(params[1])
if op64 then
if a.mode ~= "rmR" then werror("bad operand mode") end
sz = a.opsize
rex = sz == "q" and 8 or 0
opcode = 0xa1
else
if sub(a.mode, 1, 1) ~= "r" or a.opsize ~= "q" then
werror("bad operand mode")
end
op64 = params[2]
if a.reg == -1 then
vreg = a.vreg
opcode = 0xb8
else
opcode = 0xb8 + band(a.reg, 7)
end
rex = a.reg > 7 and 9 or 8
end
end
local psz, sk = wputop(sz, opcode, rex, nil, vreg)
wvreg("opcode", vreg, psz, sk)
waction("IMM_D", format("(unsigned int)(%s)", op64))
waction("IMM_D", format("(unsigned int)((%s)>>32)", op64))
end
end
------------------------------------------------------------------------------
-- Pseudo-opcodes for data storage.
local function op_data(params)
if not params then return "imm..." end
local sz = sub(params.op, 2, 2)
if sz == "l" then sz = "d" elseif sz == "a" then sz = addrsize end
for _,p in ipairs(params) do
local a = parseoperand(p, sz == "q")
if sub(a.mode, 1, 1) ~= "i" or (a.opsize and a.opsize ~= sz) then
werror("bad mode or size in `"..p.."'")
end
if a.mode == "iJ" then
wputlabel("IMM_", a.imm, 1)
elseif sz == "q" then
wputqarg(a.imm)
else
wputszarg(sz, a.imm)
end
if secpos+2 > maxsecpos then wflush() end
end
end
map_op[".byte_*"] = op_data
map_op[".sbyte_*"] = op_data
map_op[".word_*"] = op_data
map_op[".dword_*"] = op_data
map_op[".qword_*"] = op_data
map_op[".aword_*"] = op_data
map_op[".long_*"] = op_data
map_op[".quad_*"] = op_data
map_op[".addr_*"] = op_data
------------------------------------------------------------------------------
-- Pseudo-opcode to mark the position where the action list is to be emitted.
map_op[".actionlist_1"] = function(params)
if not params then return "cvar" end
local name = params[1] -- No syntax check. You get to keep the pieces.
wline(function(out) writeactions(out, name) end)
end
-- Pseudo-opcode to mark the position where the global enum is to be emitted.
map_op[".globals_1"] = function(params)
if not params then return "prefix" end
local prefix = params[1] -- No syntax check. You get to keep the pieces.
wline(function(out) writeglobals(out, prefix) end)
end
-- Pseudo-opcode to mark the position where the global names are to be emitted.
map_op[".globalnames_1"] = function(params)
if not params then return "cvar" end
local name = params[1] -- No syntax check. You get to keep the pieces.
wline(function(out) writeglobalnames(out, name) end)
end
-- Pseudo-opcode to mark the position where the extern names are to be emitted.
map_op[".externnames_1"] = function(params)
if not params then return "cvar" end
local name = params[1] -- No syntax check. You get to keep the pieces.
wline(function(out) writeexternnames(out, name) end)
end
------------------------------------------------------------------------------
-- Label pseudo-opcode (converted from trailing colon form).
map_op[".label_2"] = function(params)
if not params then return "[1-9] | ->global | =>pcexpr [, addr]" end
if secpos+2 > maxsecpos then wflush() end
local a = parseoperand(params[1])
local mode, imm = a.mode, a.imm
if type(imm) == "number" and (mode == "iJ" or (imm >= 1 and imm <= 9)) then
-- Local label (1: ... 9:) or global label (->global:).
waction("LABEL_LG", nil, 1)
wputxb(imm)
elseif mode == "iJ" then
-- PC label (=>pcexpr:).
waction("LABEL_PC", imm)
else
werror("bad label definition")
end
-- SETLABEL must immediately follow LABEL_LG/LABEL_PC.
local addr = params[2]
if addr then
local a = parseoperand(addr)
if a.mode == "iPJ" then
waction("SETLABEL", a.imm)
else
werror("bad label assignment")
end
end
end
map_op[".label_1"] = map_op[".label_2"]
------------------------------------------------------------------------------
-- Alignment pseudo-opcode.
map_op[".align_1"] = function(params)
if not params then return "numpow2" end
if secpos+1 > maxsecpos then wflush() end
local align = tonumber(params[1]) or map_opsizenum[map_opsize[params[1]]]
if align then
local x = align
-- Must be a power of 2 in the range (2 ... 256).
for i=1,8 do
x = x / 2
if x == 1 then
waction("ALIGN", nil, 1)
wputxb(align-1) -- Action byte is 2**n-1.
return
end
end
end
werror("bad alignment")
end
-- Spacing pseudo-opcode.
map_op[".space_2"] = function(params)
if not params then return "num [, filler]" end
if secpos+1 > maxsecpos then wflush() end
waction("SPACE", params[1])
local fill = params[2]
if fill then
fill = tonumber(fill)
if not fill or fill < 0 or fill > 255 then werror("bad filler") end
end
wputxb(fill or 0)
end
map_op[".space_1"] = map_op[".space_2"]
------------------------------------------------------------------------------
-- Pseudo-opcode for (primitive) type definitions (map to C types).
map_op[".type_3"] = function(params, nparams)
if not params then
return nparams == 2 and "name, ctype" or "name, ctype, reg"
end
local name, ctype, reg = params[1], params[2], params[3]
if not match(name, "^[%a_][%w_]*$") then
werror("bad type name `"..name.."'")
end
local tp = map_type[name]
if tp then
werror("duplicate type `"..name.."'")
end
if reg and not map_reg_valid_base[reg] then
werror("bad base register `"..(map_reg_rev[reg] or reg).."'")
end
-- Add #type to defines. A bit unclean to put it in map_archdef.
map_archdef["#"..name] = "sizeof("..ctype..")"
-- Add new type and emit shortcut define.
local num = ctypenum + 1
map_type[name] = {
ctype = ctype,
ctypefmt = format("Dt%X(%%s)", num),
reg = reg,
}
wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
ctypenum = num
end
map_op[".type_2"] = map_op[".type_3"]
-- Dump type definitions.
local function dumptypes(out, lvl)
local t = {}
for name in pairs(map_type) do t[#t+1] = name end
sort(t)
out:write("Type definitions:\n")
for _,name in ipairs(t) do
local tp = map_type[name]
local reg = tp.reg and map_reg_rev[tp.reg] or ""
out:write(format(" %-20s %-20s %s\n", name, tp.ctype, reg))
end
out:write("\n")
end
------------------------------------------------------------------------------
-- Set the current section.
function _M.section(num)
waction("SECTION")
wputxb(num)
wflush(true) -- SECTION is a terminal action.
end
------------------------------------------------------------------------------
-- Dump architecture description.
function _M.dumparch(out)
out:write(format("DynASM %s version %s, released %s\n\n",
_info.arch, _info.version, _info.release))
dumpregs(out)
dumpactions(out)
end
-- Dump all user defined elements.
function _M.dumpdef(out, lvl)
dumptypes(out, lvl)
dumpglobals(out, lvl)
dumpexterns(out, lvl)
end
------------------------------------------------------------------------------
-- Pass callbacks from/to the DynASM core.
function _M.passcb(wl, we, wf, ww)
wline, werror, wfatal, wwarn = wl, we, wf, ww
return wflush
end
-- Setup the arch-specific module.
function _M.setup(arch, opt)
g_arch, g_opt = arch, opt
end
-- Merge the core maps and the arch-specific maps.
function _M.mergemaps(map_coreop, map_def)
setmetatable(map_op, { __index = map_coreop })
setmetatable(map_def, { __index = map_archdef })
return map_op, map_def
end
return _M
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- DynASM. A dynamic assembler for code generation engines.
-- Originally designed and implemented for LuaJIT.
--
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved.
-- See below for full copyright notice.
------------------------------------------------------------------------------
-- Application information.
local _info = {
name = "DynASM",
description = "A dynamic assembler for code generation engines",
version = "1.5.0",
vernum = 10500,
release = "2021-05-02",
author = "Mike Pall",
url = "https://luajit.org/dynasm.html",
license = "MIT",
copyright = [[
Copyright (C) 2005-2022 Mike Pall. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[ MIT license: https://www.opensource.org/licenses/mit-license.php ]
]],
}
-- Cache library functions.
local type, pairs, ipairs = type, pairs, ipairs
local pcall, error, assert = pcall, error, assert
local _s = string
local sub, match, gmatch, gsub = _s.sub, _s.match, _s.gmatch, _s.gsub
local format, rep, upper = _s.format, _s.rep, _s.upper
local _t = table
local insert, remove, concat, sort = _t.insert, _t.remove, _t.concat, _t.sort
local exit = os.exit
local io = io
local stdin, stdout, stderr = io.stdin, io.stdout, io.stderr
------------------------------------------------------------------------------
-- Program options.
local g_opt = {}
-- Global state for current file.
local g_fname, g_curline, g_indent, g_lineno, g_synclineno, g_arch
local g_errcount = 0
-- Write buffer for output file.
local g_wbuffer, g_capbuffer
------------------------------------------------------------------------------
-- Write an output line (or callback function) to the buffer.
local function wline(line, needindent)
local buf = g_capbuffer or g_wbuffer
buf[#buf+1] = needindent and g_indent..line or line
g_synclineno = g_synclineno + 1
end
-- Write assembler line as a comment, if requestd.
local function wcomment(aline)
if g_opt.comment then
wline(g_opt.comment..aline..g_opt.endcomment, true)
end
end
-- Resync CPP line numbers.
local function wsync()
if g_synclineno ~= g_lineno and g_opt.cpp then
wline("#line "..g_lineno..' "'..g_fname..'"')
g_synclineno = g_lineno
end
end
-- Dummy action flush function. Replaced with arch-specific function later.
local function wflush(term)
end
-- Dump all buffered output lines.
local function wdumplines(out, buf)
for _,line in ipairs(buf) do
if type(line) == "string" then
assert(out:write(line, "\n"))
else
-- Special callback to dynamically insert lines after end of processing.
line(out)
end
end
end
------------------------------------------------------------------------------
-- Emit an error. Processing continues with next statement.
local function werror(msg)
error(format("%s:%s: error: %s:\n%s", g_fname, g_lineno, msg, g_curline), 0)
end
-- Emit a fatal error. Processing stops.
local function wfatal(msg)
g_errcount = "fatal"
werror(msg)
end
-- Print a warning. Processing continues.
local function wwarn(msg)
stderr:write(format("%s:%s: warning: %s:\n%s\n",
g_fname, g_lineno, msg, g_curline))
end
-- Print caught error message. But suppress excessive errors.
local function wprinterr(...)
if type(g_errcount) == "number" then
-- Regular error.
g_errcount = g_errcount + 1
if g_errcount < 21 then -- Seems to be a reasonable limit.
stderr:write(...)
elseif g_errcount == 21 then
stderr:write(g_fname,
":*: warning: too many errors (suppressed further messages).\n")
end
else
-- Fatal error.
stderr:write(...)
return true -- Stop processing.
end
end
------------------------------------------------------------------------------
-- Map holding all option handlers.
local opt_map = {}
local opt_current
-- Print error and exit with error status.
local function opterror(...)
stderr:write("dynasm.lua: ERROR: ", ...)
stderr:write("\n")
exit(1)
end
-- Get option parameter.
local function optparam(args)
local argn = args.argn
local p = args[argn]
if not p then
opterror("missing parameter for option `", opt_current, "'.")
end
args.argn = argn + 1
return p
end
------------------------------------------------------------------------------
-- Core pseudo-opcodes.
local map_coreop = {}
-- Dummy opcode map. Replaced by arch-specific map.
local map_op = {}
-- Forward declarations.
local dostmt
local readfile
------------------------------------------------------------------------------
-- Map for defines (initially empty, chains to arch-specific map).
local map_def = {}
-- Pseudo-opcode to define a substitution.
map_coreop[".define_2"] = function(params, nparams)
if not params then return nparams == 1 and "name" or "name, subst" end
local name, def = params[1], params[2] or "1"
if not match(name, "^[%a_][%w_]*$") then werror("bad or duplicate define") end
map_def[name] = def
end
map_coreop[".define_1"] = map_coreop[".define_2"]
-- Define a substitution on the command line.
function opt_map.D(args)
local namesubst = optparam(args)
local name, subst = match(namesubst, "^([%a_][%w_]*)=(.*)$")
if name then
map_def[name] = subst
elseif match(namesubst, "^[%a_][%w_]*$") then
map_def[namesubst] = "1"
else
opterror("bad define")
end
end
-- Undefine a substitution on the command line.
function opt_map.U(args)
local name = optparam(args)
if match(name, "^[%a_][%w_]*$") then
map_def[name] = nil
else
opterror("bad define")
end
end
-- Helper for definesubst.
local gotsubst
local function definesubst_one(word)
local subst = map_def[word]
if subst then gotsubst = word; return subst else return word end
end
-- Iteratively substitute defines.
local function definesubst(stmt)
-- Limit number of iterations.
for i=1,100 do
gotsubst = false
stmt = gsub(stmt, "#?[%w_]+", definesubst_one)
if not gotsubst then break end
end
if gotsubst then wfatal("recursive define involving `"..gotsubst.."'") end
return stmt
end
-- Dump all defines.
local function dumpdefines(out, lvl)
local t = {}
for name in pairs(map_def) do
t[#t+1] = name
end
sort(t)
out:write("Defines:\n")
for _,name in ipairs(t) do
local subst = map_def[name]
if g_arch then subst = g_arch.revdef(subst) end
out:write(format(" %-20s %s\n", name, subst))
end
out:write("\n")
end
------------------------------------------------------------------------------
-- Support variables for conditional assembly.
local condlevel = 0
local condstack = {}
-- Evaluate condition with a Lua expression. Substitutions already performed.
local function cond_eval(cond)
local func, err
if setfenv then
func, err = loadstring("return "..cond, "=expr")
else
-- No globals. All unknown identifiers evaluate to nil.
func, err = load("return "..cond, "=expr", "t", {})
end
if func then
if setfenv then
setfenv(func, {}) -- No globals. All unknown identifiers evaluate to nil.
end
local ok, res = pcall(func)
if ok then
if res == 0 then return false end -- Oh well.
return not not res
end
err = res
end
wfatal("bad condition: "..err)
end
-- Skip statements until next conditional pseudo-opcode at the same level.
local function stmtskip()
local dostmt_save = dostmt
local lvl = 0
dostmt = function(stmt)
local op = match(stmt, "^%s*(%S+)")
if op == ".if" then
lvl = lvl + 1
elseif lvl ~= 0 then
if op == ".endif" then lvl = lvl - 1 end
elseif op == ".elif" or op == ".else" or op == ".endif" then
dostmt = dostmt_save
dostmt(stmt)
end
end
end
-- Pseudo-opcodes for conditional assembly.
map_coreop[".if_1"] = function(params)
if not params then return "condition" end
local lvl = condlevel + 1
local res = cond_eval(params[1])
condlevel = lvl
condstack[lvl] = res
if not res then stmtskip() end
end
map_coreop[".elif_1"] = function(params)
if not params then return "condition" end
if condlevel == 0 then wfatal(".elif without .if") end
local lvl = condlevel
local res = condstack[lvl]
if res then
if res == "else" then wfatal(".elif after .else") end
else
res = cond_eval(params[1])
if res then
condstack[lvl] = res
return
end
end
stmtskip()
end
map_coreop[".else_0"] = function(params)
if condlevel == 0 then wfatal(".else without .if") end
local lvl = condlevel
local res = condstack[lvl]
condstack[lvl] = "else"
if res then
if res == "else" then wfatal(".else after .else") end
stmtskip()
end
end
map_coreop[".endif_0"] = function(params)
local lvl = condlevel
if lvl == 0 then wfatal(".endif without .if") end
condlevel = lvl - 1
end
-- Check for unfinished conditionals.
local function checkconds()
if g_errcount ~= "fatal" and condlevel ~= 0 then
wprinterr(g_fname, ":*: error: unbalanced conditional\n")
end
end
------------------------------------------------------------------------------
-- Search for a file in the given path and open it for reading.
local function pathopen(path, name)
local dirsep = package and match(package.path, "\\") and "\\" or "/"
for _,p in ipairs(path) do
local fullname = p == "" and name or p..dirsep..name
local fin = io.open(fullname, "r")
if fin then
g_fname = fullname
return fin
end
end
end
-- Include a file.
map_coreop[".include_1"] = function(params)
if not params then return "filename" end
local name = params[1]
-- Save state. Ugly, I know. but upvalues are fast.
local gf, gl, gcl, gi = g_fname, g_lineno, g_curline, g_indent
-- Read the included file.
local fatal = readfile(pathopen(g_opt.include, name) or
wfatal("include file `"..name.."' not found"))
-- Restore state.
g_synclineno = -1
g_fname, g_lineno, g_curline, g_indent = gf, gl, gcl, gi
if fatal then wfatal("in include file") end
end
-- Make .include and conditionals initially available, too.
map_op[".include_1"] = map_coreop[".include_1"]
map_op[".if_1"] = map_coreop[".if_1"]
map_op[".elif_1"] = map_coreop[".elif_1"]
map_op[".else_0"] = map_coreop[".else_0"]
map_op[".endif_0"] = map_coreop[".endif_0"]
------------------------------------------------------------------------------
-- Support variables for macros.
local mac_capture, mac_lineno, mac_name
local mac_active = {}
local mac_list = {}
-- Pseudo-opcode to define a macro.
map_coreop[".macro_*"] = function(mparams)
if not mparams then return "name [, params...]" end
-- Split off and validate macro name.
local name = remove(mparams, 1)
if not name then werror("missing macro name") end
if not (match(name, "^[%a_][%w_%.]*$") or match(name, "^%.[%w_%.]*$")) then
wfatal("bad macro name `"..name.."'")
end
-- Validate macro parameter names.
local mdup = {}
for _,mp in ipairs(mparams) do
if not match(mp, "^[%a_][%w_]*$") then
wfatal("bad macro parameter name `"..mp.."'")
end
if mdup[mp] then wfatal("duplicate macro parameter name `"..mp.."'") end
mdup[mp] = true
end
-- Check for duplicate or recursive macro definitions.
local opname = name.."_"..#mparams
if map_op[opname] or map_op[name.."_*"] then
wfatal("duplicate macro `"..name.."' ("..#mparams.." parameters)")
end
if mac_capture then wfatal("recursive macro definition") end
-- Enable statement capture.
local lines = {}
mac_lineno = g_lineno
mac_name = name
mac_capture = function(stmt) -- Statement capture function.
-- Stop macro definition with .endmacro pseudo-opcode.
if not match(stmt, "^%s*.endmacro%s*$") then
lines[#lines+1] = stmt
return
end
mac_capture = nil
mac_lineno = nil
mac_name = nil
mac_list[#mac_list+1] = opname
-- Add macro-op definition.
map_op[opname] = function(params)
if not params then return mparams, lines end
-- Protect against recursive macro invocation.
if mac_active[opname] then wfatal("recursive macro invocation") end
mac_active[opname] = true
-- Setup substitution map.
local subst = {}
for i,mp in ipairs(mparams) do subst[mp] = params[i] end
local mcom
if g_opt.maccomment and g_opt.comment then
mcom = " MACRO "..name.." ("..#mparams..")"
wcomment("{"..mcom)
end
-- Loop through all captured statements
for _,stmt in ipairs(lines) do
-- Substitute macro parameters.
local st = gsub(stmt, "[%w_]+", subst)
st = definesubst(st)
st = gsub(st, "%s*%.%.%s*", "") -- Token paste a..b.
if mcom and sub(st, 1, 1) ~= "|" then wcomment(st) end
-- Emit statement. Use a protected call for better diagnostics.
local ok, err = pcall(dostmt, st)
if not ok then
-- Add the captured statement to the error.
wprinterr(err, "\n", g_indent, "| ", stmt,
"\t[MACRO ", name, " (", #mparams, ")]\n")
end
end
if mcom then wcomment("}"..mcom) end
mac_active[opname] = nil
end
end
end
-- An .endmacro pseudo-opcode outside of a macro definition is an error.
map_coreop[".endmacro_0"] = function(params)
wfatal(".endmacro without .macro")
end
-- Dump all macros and their contents (with -PP only).
local function dumpmacros(out, lvl)
sort(mac_list)
out:write("Macros:\n")
for _,opname in ipairs(mac_list) do
local name = sub(opname, 1, -3)
local params, lines = map_op[opname]()
out:write(format(" %-20s %s\n", name, concat(params, ", ")))
if lvl > 1 then
for _,line in ipairs(lines) do
out:write(" |", line, "\n")
end
out:write("\n")
end
end
out:write("\n")
end
-- Check for unfinished macro definitions.
local function checkmacros()
if mac_capture then
wprinterr(g_fname, ":", mac_lineno,
": error: unfinished .macro `", mac_name ,"'\n")
end
end
------------------------------------------------------------------------------
-- Support variables for captures.
local cap_lineno, cap_name
local cap_buffers = {}
local cap_used = {}
-- Start a capture.
map_coreop[".capture_1"] = function(params)
if not params then return "name" end
wflush()
local name = params[1]
if not match(name, "^[%a_][%w_]*$") then
wfatal("bad capture name `"..name.."'")
end
if cap_name then
wfatal("already capturing to `"..cap_name.."' since line "..cap_lineno)
end
cap_name = name
cap_lineno = g_lineno
-- Create or continue a capture buffer and start the output line capture.
local buf = cap_buffers[name]
if not buf then buf = {}; cap_buffers[name] = buf end
g_capbuffer = buf
g_synclineno = 0
end
-- Stop a capture.
map_coreop[".endcapture_0"] = function(params)
wflush()
if not cap_name then wfatal(".endcapture without a valid .capture") end
cap_name = nil
cap_lineno = nil
g_capbuffer = nil
g_synclineno = 0
end
-- Dump a capture buffer.
map_coreop[".dumpcapture_1"] = function(params)
if not params then return "name" end
wflush()
local name = params[1]
if not match(name, "^[%a_][%w_]*$") then
wfatal("bad capture name `"..name.."'")
end
cap_used[name] = true
wline(function(out)
local buf = cap_buffers[name]
if buf then wdumplines(out, buf) end
end)
g_synclineno = 0
end
-- Dump all captures and their buffers (with -PP only).
local function dumpcaptures(out, lvl)
out:write("Captures:\n")
for name,buf in pairs(cap_buffers) do
out:write(format(" %-20s %4s)\n", name, "("..#buf))
if lvl > 1 then
local bar = rep("=", 76)
out:write(" ", bar, "\n")
for _,line in ipairs(buf) do
out:write(" ", line, "\n")
end
out:write(" ", bar, "\n\n")
end
end
out:write("\n")
end
-- Check for unfinished or unused captures.
local function checkcaptures()
if cap_name then
wprinterr(g_fname, ":", cap_lineno,
": error: unfinished .capture `", cap_name,"'\n")
return
end
for name in pairs(cap_buffers) do
if not cap_used[name] then
wprinterr(g_fname, ":*: error: missing .dumpcapture ", name ,"\n")
end
end
end
------------------------------------------------------------------------------
-- Sections names.
local map_sections = {}
-- Pseudo-opcode to define code sections.
-- TODO: Data sections, BSS sections. Needs extra C code and API.
map_coreop[".section_*"] = function(params)
if not params then return "name..." end
if #map_sections > 0 then werror("duplicate section definition") end
wflush()
for sn,name in ipairs(params) do
local opname = "."..name.."_0"
if not match(name, "^[%a][%w_]*$") or
map_op[opname] or map_op["."..name.."_*"] then
werror("bad section name `"..name.."'")
end
map_sections[#map_sections+1] = name
wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1))
map_op[opname] = function(params) g_arch.section(sn-1) end
end
wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections))
end
-- Dump all sections.
local function dumpsections(out, lvl)
out:write("Sections:\n")
for _,name in ipairs(map_sections) do
out:write(format(" %s\n", name))
end
out:write("\n")
end
------------------------------------------------------------------------------
-- Replacement for customized Lua, which lacks the package library.
local prefix = ""
if not require then
function require(name)
local fp = assert(io.open(prefix..name..".lua"))
local s = fp:read("*a")
assert(fp:close())
return assert(loadstring(s, "@"..name..".lua"))()
end
end
-- Load architecture-specific module.
local function loadarch(arch)
if not match(arch, "^[%w_]+$") then return "bad arch name" end
_G._map_def = map_def
local ok, m_arch = pcall(require, "dasm_"..arch)
if not ok then return "cannot load module: "..m_arch end
g_arch = m_arch
wflush = m_arch.passcb(wline, werror, wfatal, wwarn)
m_arch.setup(arch, g_opt)
map_op, map_def = m_arch.mergemaps(map_coreop, map_def)
end
-- Dump architecture description.
function opt_map.dumparch(args)
local name = optparam(args)
if not g_arch then
local err = loadarch(name)
if err then opterror(err) end
end
local t = {}
for name in pairs(map_coreop) do t[#t+1] = name end
for name in pairs(map_op) do t[#t+1] = name end
sort(t)
local out = stdout
local _arch = g_arch._info
out:write(format("%s version %s, released %s, %s\n",
_info.name, _info.version, _info.release, _info.url))
g_arch.dumparch(out)
local pseudo = true
out:write("Pseudo-Opcodes:\n")
for _,sname in ipairs(t) do
local name, nparam = match(sname, "^(.+)_([0-9%*])$")
if name then
if pseudo and sub(name, 1, 1) ~= "." then
out:write("\nOpcodes:\n")
pseudo = false
end
local f = map_op[sname]
local s
if nparam ~= "*" then nparam = nparam + 0 end
if nparam == 0 then
s = ""
elseif type(f) == "string" then
s = map_op[".template__"](nil, f, nparam)
else
s = f(nil, nparam)
end
if type(s) == "table" then
for _,s2 in ipairs(s) do
out:write(format(" %-12s %s\n", name, s2))
end
else
out:write(format(" %-12s %s\n", name, s))
end
end
end
out:write("\n")
exit(0)
end
-- Pseudo-opcode to set the architecture.
-- Only initially available (map_op is replaced when called).
map_op[".arch_1"] = function(params)
if not params then return "name" end
local err = loadarch(params[1])
if err then wfatal(err) end
wline(format("#if DASM_VERSION != %d", _info.vernum))
wline('#error "Version mismatch between DynASM and included encoding engine"')
wline("#endif")
end
-- Dummy .arch pseudo-opcode to improve the error report.
map_coreop[".arch_1"] = function(params)
if not params then return "name" end
wfatal("duplicate .arch statement")
end
------------------------------------------------------------------------------
-- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'.
map_coreop[".nop_*"] = function(params)
if not params then return "[ignored...]" end
end
-- Pseudo-opcodes to raise errors.
map_coreop[".error_1"] = function(params)
if not params then return "message" end
werror(params[1])
end
map_coreop[".fatal_1"] = function(params)
if not params then return "message" end
wfatal(params[1])
end
-- Dump all user defined elements.
local function dumpdef(out)
local lvl = g_opt.dumpdef
if lvl == 0 then return end
dumpsections(out, lvl)
dumpdefines(out, lvl)
if g_arch then g_arch.dumpdef(out, lvl) end
dumpmacros(out, lvl)
dumpcaptures(out, lvl)
end
------------------------------------------------------------------------------
-- Helper for splitstmt.
local splitlvl
local function splitstmt_one(c)
if c == "(" then
splitlvl = ")"..splitlvl
elseif c == "[" then
splitlvl = "]"..splitlvl
elseif c == "{" then
splitlvl = "}"..splitlvl
elseif c == ")" or c == "]" or c == "}" then
if sub(splitlvl, 1, 1) ~= c then werror("unbalanced (), [] or {}") end
splitlvl = sub(splitlvl, 2)
elseif splitlvl == "" then
return " \0 "
end
return c
end
-- Split statement into (pseudo-)opcode and params.
local function splitstmt(stmt)
-- Convert label with trailing-colon into .label statement.
local label = match(stmt, "^%s*(.+):%s*$")
if label then return ".label", {label} end
-- Split at commas and equal signs, but obey parentheses and brackets.
splitlvl = ""
stmt = gsub(stmt, "[,%(%)%[%]{}]", splitstmt_one)
if splitlvl ~= "" then werror("unbalanced () or []") end
-- Split off opcode.
local op, other = match(stmt, "^%s*([^%s%z]+)%s*(.*)$")
if not op then werror("bad statement syntax") end
-- Split parameters.
local params = {}
for p in gmatch(other, "%s*(%Z+)%z?") do
params[#params+1] = gsub(p, "%s+$", "")
end
if #params > 16 then werror("too many parameters") end
params.op = op
return op, params
end
-- Process a single statement.
dostmt = function(stmt)
-- Ignore empty statements.
if match(stmt, "^%s*$") then return end
-- Capture macro defs before substitution.
if mac_capture then return mac_capture(stmt) end
stmt = definesubst(stmt)
-- Emit C code without parsing the line.
if sub(stmt, 1, 1) == "|" then
local tail = sub(stmt, 2)
wflush()
if sub(tail, 1, 2) == "//" then wcomment(tail) else wline(tail, true) end
return
end
-- Split into (pseudo-)opcode and params.
local op, params = splitstmt(stmt)
-- Get opcode handler (matching # of parameters or generic handler).
local f = map_op[op.."_"..#params] or map_op[op.."_*"]
if not f then
if not g_arch then wfatal("first statement must be .arch") end
-- Improve error report.
for i=0,9 do
if map_op[op.."_"..i] then
werror("wrong number of parameters for `"..op.."'")
end
end
werror("unknown statement `"..op.."'")
end
-- Call opcode handler or special handler for template strings.
if type(f) == "string" then
map_op[".template__"](params, f)
else
f(params)
end
end
-- Process a single line.
local function doline(line)
if g_opt.flushline then wflush() end
-- Assembler line?
local indent, aline = match(line, "^(%s*)%//|(.*)$")
if not aline then
-- No, plain C code line, need to flush first.
wflush()
wsync()
wline(line, false)
return
end
g_indent = indent -- Remember current line indentation.
-- Emit C code (even from macros). Avoids echo and line parsing.
if sub(aline, 1, 1) == "|" then
if not mac_capture then
wsync()
elseif g_opt.comment then
wsync()
wcomment(aline)
end
dostmt(aline)
return
end
-- Echo assembler line as a comment.
if g_opt.comment then
wsync()
wcomment(aline)
end
-- Strip assembler comments.
aline = gsub(aline, "//.*$", "")
-- Split line into statements at semicolons.
if match(aline, ";") then
for stmt in gmatch(aline, "[^;]+") do dostmt(stmt) end
else
dostmt(aline)
end
end
------------------------------------------------------------------------------
-- Write DynASM header.
local function dasmhead(out)
out:write(format([[
/*
** This file has been pre-processed with DynASM.
** %s
** DynASM version %s, DynASM %s version %s
** DO NOT EDIT! The original file is in "%s".
*/
]], _info.url,
_info.version, g_arch._info.arch, g_arch._info.version,
g_fname))
end
-- Read input file.
readfile = function(fin)
g_indent = ""
g_lineno = 0
g_synclineno = -1
-- Process all lines.
for line in fin:lines() do
g_lineno = g_lineno + 1
g_curline = line
local ok, err = pcall(doline, line)
if not ok and wprinterr(err, "\n") then return true end
end
wflush()
-- Close input file.
assert(fin == stdin or fin:close())
end
-- Write output file.
local function writefile(outfile)
local fout
-- Open output file.
if outfile == nil or outfile == "-" then
fout = stdout
else
fout = assert(io.open(outfile, "w"))
end
-- Write all buffered lines
wdumplines(fout, g_wbuffer)
-- Close output file.
assert(fout == stdout or fout:close())
-- Optionally dump definitions.
dumpdef(fout == stdout and stderr or stdout)
end
-- Translate an input file to an output file.
local function translate(infile, outfile)
g_wbuffer = {}
g_indent = ""
g_lineno = 0
g_synclineno = -1
-- Put header.
wline(dasmhead)
-- Read input file.
local fin
if infile == "-" then
g_fname = "(stdin)"
fin = stdin
else
g_fname = infile
fin = assert(io.open(infile, "r"))
end
readfile(fin)
-- Check for errors.
if not g_arch then
wprinterr(g_fname, ":*: error: missing .arch directive\n")
end
checkconds()
checkmacros()
checkcaptures()
if g_errcount ~= 0 then
stderr:write(g_fname, ":*: info: ", g_errcount, " error",
(type(g_errcount) == "number" and g_errcount > 1) and "s" or "",
" in input file -- no output file generated.\n")
dumpdef(stderr)
exit(1)
end
-- Write output file.
writefile(outfile)
end
------------------------------------------------------------------------------
-- Print help text.
function opt_map.help()
stdout:write("DynASM -- ", _info.description, ".\n")
stdout:write("DynASM ", _info.version, " ", _info.release, " ", _info.url, "\n")
stdout:write[[
Usage: dynasm [OPTION]... INFILE.dasc|-
-h, --help Display this help text.
-V, --version Display version and copyright information.
-o, --outfile FILE Output file name (default is stdout).
-I, --include DIR Add directory to the include search path.
-c, --ccomment Use /* */ comments for assembler lines.
-C, --cppcomment Use // comments for assembler lines (default).
-N, --nocomment Suppress assembler lines in output.
-M, --maccomment Show macro expansions as comments (default off).
-L, --nolineno Suppress CPP line number information in output.
-F, --flushline Flush action list for every line.
-D NAME[=SUBST] Define a substitution.
-U NAME Undefine a substitution.
-P, --dumpdef Dump defines, macros, etc. Repeat for more output.
-A, --dumparch ARCH Load architecture ARCH and dump description.
]]
exit(0)
end
-- Print version information.
function opt_map.version()
stdout:write(format("%s version %s, released %s\n%s\n\n%s",
_info.name, _info.version, _info.release, _info.url, _info.copyright))
exit(0)
end
-- Misc. options.
function opt_map.outfile(args) g_opt.outfile = optparam(args) end
function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end
function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end
function opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" end
function opt_map.nocomment() g_opt.comment = false end
function opt_map.maccomment() g_opt.maccomment = true end
function opt_map.nolineno() g_opt.cpp = false end
function opt_map.flushline() g_opt.flushline = true end
function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end
------------------------------------------------------------------------------
-- Short aliases for long options.
local opt_alias = {
h = "help", ["?"] = "help", V = "version",
o = "outfile", I = "include",
c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment",
L = "nolineno", F = "flushline",
P = "dumpdef", A = "dumparch",
}
-- Parse single option.
local function parseopt(opt, args)
opt_current = #opt == 1 and "-"..opt or "--"..opt
local f = opt_map[opt] or opt_map[opt_alias[opt]]
if not f then
opterror("unrecognized option `", opt_current, "'. Try `--help'.\n")
end
f(args)
end
-- Parse arguments.
local function parseargs(args)
-- Default options.
g_opt.comment = "//|"
g_opt.endcomment = ""
g_opt.cpp = true
g_opt.dumpdef = 0
g_opt.include = { "" }
-- Process all option arguments.
args.argn = 1
repeat
local a = args[args.argn]
if not a then break end
local lopt, opt = match(a, "^%-(%-?)(.+)")
if not opt then break end
args.argn = args.argn + 1
if lopt == "" then
-- Loop through short options.
for o in gmatch(opt, ".") do parseopt(o, args) end
else
-- Long option.
parseopt(opt, args)
end
until false
-- Check for proper number of arguments.
local nargs = #args - args.argn + 1
if nargs ~= 1 then
if nargs == 0 then
if g_opt.dumpdef > 0 then return dumpdef(stdout) end
end
opt_map.help()
end
-- Translate a single input file to a single output file
-- TODO: Handle multiple files?
translate(args[args.argn], g_opt.outfile)
end
------------------------------------------------------------------------------
-- Add the directory dynasm.lua resides in to the Lua module search path.
local arg = arg
if arg and arg[0] then
prefix = match(arg[0], "^(.*[/\\])")
if package and prefix then package.path = prefix.."?.lua;"..package.path end
end
-- Start DynASM.
parseargs{...}
------------------------------------------------------------------------------
/* This is a heavily customized and minimized copy of Lua 5.1.5. */
/* It's only used to build LuaJIT. It does NOT have all standard functions! */
/******************************************************************************
* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#ifdef _MSC_VER
typedef unsigned __int64 U64;
#else
typedef unsigned long long U64;
#endif
int _CRT_glob = 0;
#include <stddef.h>
#include <stdarg.h>
#include <limits.h>
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <errno.h>
#include <time.h>
typedef enum{
TM_INDEX,
TM_NEWINDEX,
TM_GC,
TM_MODE,
TM_EQ,
TM_ADD,
TM_SUB,
TM_MUL,
TM_DIV,
TM_MOD,
TM_POW,
TM_UNM,
TM_LEN,
TM_LT,
TM_LE,
TM_CONCAT,
TM_CALL,
TM_N
}TMS;
enum OpMode{iABC,iABx,iAsBx};
typedef enum{
OP_MOVE,
OP_LOADK,
OP_LOADBOOL,
OP_LOADNIL,
OP_GETUPVAL,
OP_GETGLOBAL,
OP_GETTABLE,
OP_SETGLOBAL,
OP_SETUPVAL,
OP_SETTABLE,
OP_NEWTABLE,
OP_SELF,
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
OP_MOD,
OP_POW,
OP_UNM,
OP_NOT,
OP_LEN,
OP_CONCAT,
OP_JMP,
OP_EQ,
OP_LT,
OP_LE,
OP_TEST,
OP_TESTSET,
OP_CALL,
OP_TAILCALL,
OP_RETURN,
OP_FORLOOP,
OP_FORPREP,
OP_TFORLOOP,
OP_SETLIST,
OP_CLOSE,
OP_CLOSURE,
OP_VARARG
}OpCode;
enum OpArgMask{
OpArgN,
OpArgU,
OpArgR,
OpArgK
};
typedef enum{
VVOID,
VNIL,
VTRUE,
VFALSE,
VK,
VKNUM,
VLOCAL,
VUPVAL,
VGLOBAL,
VINDEXED,
VJMP,
VRELOCABLE,
VNONRELOC,
VCALL,
VVARARG
}expkind;
enum RESERVED{
TK_AND=257,TK_BREAK,
TK_DO,TK_ELSE,TK_ELSEIF,TK_END,TK_FALSE,TK_FOR,TK_FUNCTION,
TK_IF,TK_IN,TK_LOCAL,TK_NIL,TK_NOT,TK_OR,TK_REPEAT,
TK_RETURN,TK_THEN,TK_TRUE,TK_UNTIL,TK_WHILE,
TK_CONCAT,TK_DOTS,TK_EQ,TK_GE,TK_LE,TK_NE,TK_NUMBER,
TK_NAME,TK_STRING,TK_EOS
};
typedef enum BinOpr{
OPR_ADD,OPR_SUB,OPR_MUL,OPR_DIV,OPR_MOD,OPR_POW,
OPR_CONCAT,
OPR_NE,OPR_EQ,
OPR_LT,OPR_LE,OPR_GT,OPR_GE,
OPR_AND,OPR_OR,
OPR_NOBINOPR
}BinOpr;
typedef enum UnOpr{OPR_MINUS,OPR_NOT,OPR_LEN,OPR_NOUNOPR}UnOpr;
#define LUA_QL(x)"'"x"'"
#define luai_apicheck(L,o){(void)L;}
#define lua_number2str(s,n)sprintf((s),"%.14g",(n))
#define lua_str2number(s,p)strtod((s),(p))
#define luai_numadd(a,b)((a)+(b))
#define luai_numsub(a,b)((a)-(b))
#define luai_nummul(a,b)((a)*(b))
#define luai_numdiv(a,b)((a)/(b))
#define luai_nummod(a,b)((a)-floor((a)/(b))*(b))
#define luai_numpow(a,b)(pow(a,b))
#define luai_numunm(a)(-(a))
#define luai_numeq(a,b)((a)==(b))
#define luai_numlt(a,b)((a)<(b))
#define luai_numle(a,b)((a)<=(b))
#define luai_numisnan(a)(!luai_numeq((a),(a)))
#define lua_number2int(i,d)((i)=(int)(d))
#define lua_number2integer(i,d)((i)=(lua_Integer)(d))
#define LUAI_THROW(L,c)longjmp((c)->b,1)
#define LUAI_TRY(L,c,a)if(setjmp((c)->b)==0){a}
#define lua_pclose(L,file)((void)((void)L,file),0)
#define lua_upvalueindex(i)((-10002)-(i))
typedef struct lua_State lua_State;
typedef int(*lua_CFunction)(lua_State*L);
typedef const char*(*lua_Reader)(lua_State*L,void*ud,size_t*sz);
typedef void*(*lua_Alloc)(void*ud,void*ptr,size_t osize,size_t nsize);
typedef double lua_Number;
typedef ptrdiff_t lua_Integer;
static void lua_settop(lua_State*L,int idx);
static int lua_type(lua_State*L,int idx);
static const char* lua_tolstring(lua_State*L,int idx,size_t*len);
static size_t lua_objlen(lua_State*L,int idx);
static void lua_pushlstring(lua_State*L,const char*s,size_t l);
static void lua_pushcclosure(lua_State*L,lua_CFunction fn,int n);
static void lua_createtable(lua_State*L,int narr,int nrec);
static void lua_setfield(lua_State*L,int idx,const char*k);
#define lua_pop(L,n)lua_settop(L,-(n)-1)
#define lua_newtable(L)lua_createtable(L,0,0)
#define lua_pushcfunction(L,f)lua_pushcclosure(L,(f),0)
#define lua_strlen(L,i)lua_objlen(L,(i))
#define lua_isfunction(L,n)(lua_type(L,(n))==6)
#define lua_istable(L,n)(lua_type(L,(n))==5)
#define lua_isnil(L,n)(lua_type(L,(n))==0)
#define lua_isboolean(L,n)(lua_type(L,(n))==1)
#define lua_isnone(L,n)(lua_type(L,(n))==(-1))
#define lua_isnoneornil(L,n)(lua_type(L,(n))<=0)
#define lua_pushliteral(L,s)lua_pushlstring(L,""s,(sizeof(s)/sizeof(char))-1)
#define lua_setglobal(L,s)lua_setfield(L,(-10002),(s))
#define lua_tostring(L,i)lua_tolstring(L,(i),NULL)
typedef struct lua_Debug lua_Debug;
typedef void(*lua_Hook)(lua_State*L,lua_Debug*ar);
struct lua_Debug{
int event;
const char*name;
const char*namewhat;
const char*what;
const char*source;
int currentline;
int nups;
int linedefined;
int lastlinedefined;
char short_src[60];
int i_ci;
};
typedef unsigned int lu_int32;
typedef size_t lu_mem;
typedef ptrdiff_t l_mem;
typedef unsigned char lu_byte;
#define IntPoint(p)((unsigned int)(lu_mem)(p))
typedef union{double u;void*s;long l;}L_Umaxalign;
typedef double l_uacNumber;
#define check_exp(c,e)(e)
#define UNUSED(x)((void)(x))
#define cast(t,exp)((t)(exp))
#define cast_byte(i)cast(lu_byte,(i))
#define cast_num(i)cast(lua_Number,(i))
#define cast_int(i)cast(int,(i))
typedef lu_int32 Instruction;
#define condhardstacktests(x)((void)0)
typedef union GCObject GCObject;
typedef struct GCheader{
GCObject*next;lu_byte tt;lu_byte marked;
}GCheader;
typedef union{
GCObject*gc;
void*p;
lua_Number n;
int b;
}Value;
typedef struct lua_TValue{
Value value;int tt;
}TValue;
#define ttisnil(o)(ttype(o)==0)
#define ttisnumber(o)(ttype(o)==3)
#define ttisstring(o)(ttype(o)==4)
#define ttistable(o)(ttype(o)==5)
#define ttisfunction(o)(ttype(o)==6)
#define ttisboolean(o)(ttype(o)==1)
#define ttisuserdata(o)(ttype(o)==7)
#define ttisthread(o)(ttype(o)==8)
#define ttislightuserdata(o)(ttype(o)==2)
#define ttype(o)((o)->tt)
#define gcvalue(o)check_exp(iscollectable(o),(o)->value.gc)
#define pvalue(o)check_exp(ttislightuserdata(o),(o)->value.p)
#define nvalue(o)check_exp(ttisnumber(o),(o)->value.n)
#define rawtsvalue(o)check_exp(ttisstring(o),&(o)->value.gc->ts)
#define tsvalue(o)(&rawtsvalue(o)->tsv)
#define rawuvalue(o)check_exp(ttisuserdata(o),&(o)->value.gc->u)
#define uvalue(o)(&rawuvalue(o)->uv)
#define clvalue(o)check_exp(ttisfunction(o),&(o)->value.gc->cl)
#define hvalue(o)check_exp(ttistable(o),&(o)->value.gc->h)
#define bvalue(o)check_exp(ttisboolean(o),(o)->value.b)
#define thvalue(o)check_exp(ttisthread(o),&(o)->value.gc->th)
#define l_isfalse(o)(ttisnil(o)||(ttisboolean(o)&&bvalue(o)==0))
#define checkconsistency(obj)
#define checkliveness(g,obj)
#define setnilvalue(obj)((obj)->tt=0)
#define setnvalue(obj,x){TValue*i_o=(obj);i_o->value.n=(x);i_o->tt=3;}
#define setbvalue(obj,x){TValue*i_o=(obj);i_o->value.b=(x);i_o->tt=1;}
#define setsvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=4;checkliveness(G(L),i_o);}
#define setuvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=7;checkliveness(G(L),i_o);}
#define setthvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=8;checkliveness(G(L),i_o);}
#define setclvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=6;checkliveness(G(L),i_o);}
#define sethvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=5;checkliveness(G(L),i_o);}
#define setptvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=(8+1);checkliveness(G(L),i_o);}
#define setobj(L,obj1,obj2){const TValue*o2=(obj2);TValue*o1=(obj1);o1->value=o2->value;o1->tt=o2->tt;checkliveness(G(L),o1);}
#define setttype(obj,tt)(ttype(obj)=(tt))
#define iscollectable(o)(ttype(o)>=4)
typedef TValue*StkId;
typedef union TString{
L_Umaxalign dummy;
struct{
GCObject*next;lu_byte tt;lu_byte marked;
lu_byte reserved;
unsigned int hash;
size_t len;
}tsv;
}TString;
#define getstr(ts)cast(const char*,(ts)+1)
#define svalue(o)getstr(rawtsvalue(o))
typedef union Udata{
L_Umaxalign dummy;
struct{
GCObject*next;lu_byte tt;lu_byte marked;
struct Table*metatable;
struct Table*env;
size_t len;
}uv;
}Udata;
typedef struct Proto{
GCObject*next;lu_byte tt;lu_byte marked;
TValue*k;
Instruction*code;
struct Proto**p;
int*lineinfo;
struct LocVar*locvars;
TString**upvalues;
TString*source;
int sizeupvalues;
int sizek;
int sizecode;
int sizelineinfo;
int sizep;
int sizelocvars;
int linedefined;
int lastlinedefined;
GCObject*gclist;
lu_byte nups;
lu_byte numparams;
lu_byte is_vararg;
lu_byte maxstacksize;
}Proto;
typedef struct LocVar{
TString*varname;
int startpc;
int endpc;
}LocVar;
typedef struct UpVal{
GCObject*next;lu_byte tt;lu_byte marked;
TValue*v;
union{
TValue value;
struct{
struct UpVal*prev;
struct UpVal*next;
}l;
}u;
}UpVal;
typedef struct CClosure{
GCObject*next;lu_byte tt;lu_byte marked;lu_byte isC;lu_byte nupvalues;GCObject*gclist;struct Table*env;
lua_CFunction f;
TValue upvalue[1];
}CClosure;
typedef struct LClosure{
GCObject*next;lu_byte tt;lu_byte marked;lu_byte isC;lu_byte nupvalues;GCObject*gclist;struct Table*env;
struct Proto*p;
UpVal*upvals[1];
}LClosure;
typedef union Closure{
CClosure c;
LClosure l;
}Closure;
#define iscfunction(o)(ttype(o)==6&&clvalue(o)->c.isC)
typedef union TKey{
struct{
Value value;int tt;
struct Node*next;
}nk;
TValue tvk;
}TKey;
typedef struct Node{
TValue i_val;
TKey i_key;
}Node;
typedef struct Table{
GCObject*next;lu_byte tt;lu_byte marked;
lu_byte flags;
lu_byte lsizenode;
struct Table*metatable;
TValue*array;
Node*node;
Node*lastfree;
GCObject*gclist;
int sizearray;
}Table;
#define lmod(s,size)(check_exp((size&(size-1))==0,(cast(int,(s)&((size)-1)))))
#define twoto(x)((size_t)1<<(x))
#define sizenode(t)(twoto((t)->lsizenode))
static const TValue luaO_nilobject_;
#define ceillog2(x)(luaO_log2((x)-1)+1)
static int luaO_log2(unsigned int x);
#define gfasttm(g,et,e)((et)==NULL?NULL:((et)->flags&(1u<<(e)))?NULL:luaT_gettm(et,e,(g)->tmname[e]))
#define fasttm(l,et,e)gfasttm(G(l),et,e)
static const TValue*luaT_gettm(Table*events,TMS event,TString*ename);
#define luaM_reallocv(L,b,on,n,e)((cast(size_t,(n)+1)<=((size_t)(~(size_t)0)-2)/(e))?luaM_realloc_(L,(b),(on)*(e),(n)*(e)):luaM_toobig(L))
#define luaM_freemem(L,b,s)luaM_realloc_(L,(b),(s),0)
#define luaM_free(L,b)luaM_realloc_(L,(b),sizeof(*(b)),0)
#define luaM_freearray(L,b,n,t)luaM_reallocv(L,(b),n,0,sizeof(t))
#define luaM_malloc(L,t)luaM_realloc_(L,NULL,0,(t))
#define luaM_new(L,t)cast(t*,luaM_malloc(L,sizeof(t)))
#define luaM_newvector(L,n,t)cast(t*,luaM_reallocv(L,NULL,0,n,sizeof(t)))
#define luaM_growvector(L,v,nelems,size,t,limit,e)if((nelems)+1>(size))((v)=cast(t*,luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
#define luaM_reallocvector(L,v,oldn,n,t)((v)=cast(t*,luaM_reallocv(L,v,oldn,n,sizeof(t))))
static void*luaM_realloc_(lua_State*L,void*block,size_t oldsize,
size_t size);
static void*luaM_toobig(lua_State*L);
static void*luaM_growaux_(lua_State*L,void*block,int*size,
size_t size_elem,int limit,
const char*errormsg);
typedef struct Zio ZIO;
#define char2int(c)cast(int,cast(unsigned char,(c)))
#define zgetc(z)(((z)->n--)>0?char2int(*(z)->p++):luaZ_fill(z))
typedef struct Mbuffer{
char*buffer;
size_t n;
size_t buffsize;
}Mbuffer;
#define luaZ_initbuffer(L,buff)((buff)->buffer=NULL,(buff)->buffsize=0)
#define luaZ_buffer(buff)((buff)->buffer)
#define luaZ_sizebuffer(buff)((buff)->buffsize)
#define luaZ_bufflen(buff)((buff)->n)
#define luaZ_resetbuffer(buff)((buff)->n=0)
#define luaZ_resizebuffer(L,buff,size)(luaM_reallocvector(L,(buff)->buffer,(buff)->buffsize,size,char),(buff)->buffsize=size)
#define luaZ_freebuffer(L,buff)luaZ_resizebuffer(L,buff,0)
struct Zio{
size_t n;
const char*p;
lua_Reader reader;
void*data;
lua_State*L;
};
static int luaZ_fill(ZIO*z);
struct lua_longjmp;
#define gt(L)(&L->l_gt)
#define registry(L)(&G(L)->l_registry)
typedef struct stringtable{
GCObject**hash;
lu_int32 nuse;
int size;
}stringtable;
typedef struct CallInfo{
StkId base;
StkId func;
StkId top;
const Instruction*savedpc;
int nresults;
int tailcalls;
}CallInfo;
#define curr_func(L)(clvalue(L->ci->func))
#define ci_func(ci)(clvalue((ci)->func))
#define f_isLua(ci)(!ci_func(ci)->c.isC)
#define isLua(ci)(ttisfunction((ci)->func)&&f_isLua(ci))
typedef struct global_State{
stringtable strt;
lua_Alloc frealloc;
void*ud;
lu_byte currentwhite;
lu_byte gcstate;
int sweepstrgc;
GCObject*rootgc;
GCObject**sweepgc;
GCObject*gray;
GCObject*grayagain;
GCObject*weak;
GCObject*tmudata;
Mbuffer buff;
lu_mem GCthreshold;
lu_mem totalbytes;
lu_mem estimate;
lu_mem gcdept;
int gcpause;
int gcstepmul;
lua_CFunction panic;
TValue l_registry;
struct lua_State*mainthread;
UpVal uvhead;
struct Table*mt[(8+1)];
TString*tmname[TM_N];
}global_State;
struct lua_State{
GCObject*next;lu_byte tt;lu_byte marked;
lu_byte status;
StkId top;
StkId base;
global_State*l_G;
CallInfo*ci;
const Instruction*savedpc;
StkId stack_last;
StkId stack;
CallInfo*end_ci;
CallInfo*base_ci;
int stacksize;
int size_ci;
unsigned short nCcalls;
unsigned short baseCcalls;
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
TValue l_gt;
TValue env;
GCObject*openupval;
GCObject*gclist;
struct lua_longjmp*errorJmp;
ptrdiff_t errfunc;
};
#define G(L)(L->l_G)
union GCObject{
GCheader gch;
union TString ts;
union Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct UpVal uv;
struct lua_State th;
};
#define rawgco2ts(o)check_exp((o)->gch.tt==4,&((o)->ts))
#define gco2ts(o)(&rawgco2ts(o)->tsv)
#define rawgco2u(o)check_exp((o)->gch.tt==7,&((o)->u))
#define gco2u(o)(&rawgco2u(o)->uv)
#define gco2cl(o)check_exp((o)->gch.tt==6,&((o)->cl))
#define gco2h(o)check_exp((o)->gch.tt==5,&((o)->h))
#define gco2p(o)check_exp((o)->gch.tt==(8+1),&((o)->p))
#define gco2uv(o)check_exp((o)->gch.tt==(8+2),&((o)->uv))
#define ngcotouv(o)check_exp((o)==NULL||(o)->gch.tt==(8+2),&((o)->uv))
#define gco2th(o)check_exp((o)->gch.tt==8,&((o)->th))
#define obj2gco(v)(cast(GCObject*,(v)))
static void luaE_freethread(lua_State*L,lua_State*L1);
#define pcRel(pc,p)(cast(int,(pc)-(p)->code)-1)
#define getline_(f,pc)(((f)->lineinfo)?(f)->lineinfo[pc]:0)
#define resethookcount(L)(L->hookcount=L->basehookcount)
static void luaG_typeerror(lua_State*L,const TValue*o,
const char*opname);
static void luaG_runerror(lua_State*L,const char*fmt,...);
#define luaD_checkstack(L,n)if((char*)L->stack_last-(char*)L->top<=(n)*(int)sizeof(TValue))luaD_growstack(L,n);else condhardstacktests(luaD_reallocstack(L,L->stacksize-5-1));
#define incr_top(L){luaD_checkstack(L,1);L->top++;}
#define savestack(L,p)((char*)(p)-(char*)L->stack)
#define restorestack(L,n)((TValue*)((char*)L->stack+(n)))
#define saveci(L,p)((char*)(p)-(char*)L->base_ci)
#define restoreci(L,n)((CallInfo*)((char*)L->base_ci+(n)))
typedef void(*Pfunc)(lua_State*L,void*ud);
static int luaD_poscall(lua_State*L,StkId firstResult);
static void luaD_reallocCI(lua_State*L,int newsize);
static void luaD_reallocstack(lua_State*L,int newsize);
static void luaD_growstack(lua_State*L,int n);
static void luaD_throw(lua_State*L,int errcode);
static void*luaM_growaux_(lua_State*L,void*block,int*size,size_t size_elems,
int limit,const char*errormsg){
void*newblock;
int newsize;
if(*size>=limit/2){
if(*size>=limit)
luaG_runerror(L,errormsg);
newsize=limit;
}
else{
newsize=(*size)*2;
if(newsize<4)
newsize=4;
}
newblock=luaM_reallocv(L,block,*size,newsize,size_elems);
*size=newsize;
return newblock;
}
static void*luaM_toobig(lua_State*L){
luaG_runerror(L,"memory allocation error: block too big");
return NULL;
}
static void*luaM_realloc_(lua_State*L,void*block,size_t osize,size_t nsize){
global_State*g=G(L);
block=(*g->frealloc)(g->ud,block,osize,nsize);
if(block==NULL&&nsize>0)
luaD_throw(L,4);
g->totalbytes=(g->totalbytes-osize)+nsize;
return block;
}
#define resetbits(x,m)((x)&=cast(lu_byte,~(m)))
#define setbits(x,m)((x)|=(m))
#define testbits(x,m)((x)&(m))
#define bitmask(b)(1<<(b))
#define bit2mask(b1,b2)(bitmask(b1)|bitmask(b2))
#define l_setbit(x,b)setbits(x,bitmask(b))
#define resetbit(x,b)resetbits(x,bitmask(b))
#define testbit(x,b)testbits(x,bitmask(b))
#define set2bits(x,b1,b2)setbits(x,(bit2mask(b1,b2)))
#define reset2bits(x,b1,b2)resetbits(x,(bit2mask(b1,b2)))
#define test2bits(x,b1,b2)testbits(x,(bit2mask(b1,b2)))
#define iswhite(x)test2bits((x)->gch.marked,0,1)
#define isblack(x)testbit((x)->gch.marked,2)
#define isgray(x)(!isblack(x)&&!iswhite(x))
#define otherwhite(g)(g->currentwhite^bit2mask(0,1))
#define isdead(g,v)((v)->gch.marked&otherwhite(g)&bit2mask(0,1))
#define changewhite(x)((x)->gch.marked^=bit2mask(0,1))
#define gray2black(x)l_setbit((x)->gch.marked,2)
#define valiswhite(x)(iscollectable(x)&&iswhite(gcvalue(x)))
#define luaC_white(g)cast(lu_byte,(g)->currentwhite&bit2mask(0,1))
#define luaC_checkGC(L){condhardstacktests(luaD_reallocstack(L,L->stacksize-5-1));if(G(L)->totalbytes>=G(L)->GCthreshold)luaC_step(L);}
#define luaC_barrier(L,p,v){if(valiswhite(v)&&isblack(obj2gco(p)))luaC_barrierf(L,obj2gco(p),gcvalue(v));}
#define luaC_barriert(L,t,v){if(valiswhite(v)&&isblack(obj2gco(t)))luaC_barrierback(L,t);}
#define luaC_objbarrier(L,p,o){if(iswhite(obj2gco(o))&&isblack(obj2gco(p)))luaC_barrierf(L,obj2gco(p),obj2gco(o));}
#define luaC_objbarriert(L,t,o){if(iswhite(obj2gco(o))&&isblack(obj2gco(t)))luaC_barrierback(L,t);}
static void luaC_step(lua_State*L);
static void luaC_link(lua_State*L,GCObject*o,lu_byte tt);
static void luaC_linkupval(lua_State*L,UpVal*uv);
static void luaC_barrierf(lua_State*L,GCObject*o,GCObject*v);
static void luaC_barrierback(lua_State*L,Table*t);
#define sizestring(s)(sizeof(union TString)+((s)->len+1)*sizeof(char))
#define sizeudata(u)(sizeof(union Udata)+(u)->len)
#define luaS_new(L,s)(luaS_newlstr(L,s,strlen(s)))
#define luaS_newliteral(L,s)(luaS_newlstr(L,""s,(sizeof(s)/sizeof(char))-1))
#define luaS_fix(s)l_setbit((s)->tsv.marked,5)
static TString*luaS_newlstr(lua_State*L,const char*str,size_t l);
#define tostring(L,o)((ttype(o)==4)||(luaV_tostring(L,o)))
#define tonumber(o,n)(ttype(o)==3||(((o)=luaV_tonumber(o,n))!=NULL))
#define equalobj(L,o1,o2)(ttype(o1)==ttype(o2)&&luaV_equalval(L,o1,o2))
static int luaV_equalval(lua_State*L,const TValue*t1,const TValue*t2);
static const TValue*luaV_tonumber(const TValue*obj,TValue*n);
static int luaV_tostring(lua_State*L,StkId obj);
static void luaV_execute(lua_State*L,int nexeccalls);
static void luaV_concat(lua_State*L,int total,int last);
static const TValue luaO_nilobject_={{NULL},0};
static int luaO_int2fb(unsigned int x){
int e=0;
while(x>=16){
x=(x+1)>>1;
e++;
}
if(x<8)return x;
else return((e+1)<<3)|(cast_int(x)-8);
}
static int luaO_fb2int(int x){
int e=(x>>3)&31;
if(e==0)return x;
else return((x&7)+8)<<(e-1);
}
static int luaO_log2(unsigned int x){
static const lu_byte log_2[256]={
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
};
int l=-1;
while(x>=256){l+=8;x>>=8;}
return l+log_2[x];
}
static int luaO_rawequalObj(const TValue*t1,const TValue*t2){
if(ttype(t1)!=ttype(t2))return 0;
else switch(ttype(t1)){
case 0:
return 1;
case 3:
return luai_numeq(nvalue(t1),nvalue(t2));
case 1:
return bvalue(t1)==bvalue(t2);
case 2:
return pvalue(t1)==pvalue(t2);
default:
return gcvalue(t1)==gcvalue(t2);
}
}
static int luaO_str2d(const char*s,lua_Number*result){
char*endptr;
*result=lua_str2number(s,&endptr);
if(endptr==s)return 0;
if(*endptr=='x'||*endptr=='X')
*result=cast_num(strtoul(s,&endptr,16));
if(*endptr=='\0')return 1;
while(isspace(cast(unsigned char,*endptr)))endptr++;
if(*endptr!='\0')return 0;
return 1;
}
static void pushstr(lua_State*L,const char*str){
setsvalue(L,L->top,luaS_new(L,str));
incr_top(L);
}
static const char*luaO_pushvfstring(lua_State*L,const char*fmt,va_list argp){
int n=1;
pushstr(L,"");
for(;;){
const char*e=strchr(fmt,'%');
if(e==NULL)break;
setsvalue(L,L->top,luaS_newlstr(L,fmt,e-fmt));
incr_top(L);
switch(*(e+1)){
case's':{
const char*s=va_arg(argp,char*);
if(s==NULL)s="(null)";
pushstr(L,s);
break;
}
case'c':{
char buff[2];
buff[0]=cast(char,va_arg(argp,int));
buff[1]='\0';
pushstr(L,buff);
break;
}
case'd':{
setnvalue(L->top,cast_num(va_arg(argp,int)));
incr_top(L);
break;
}
case'f':{
setnvalue(L->top,cast_num(va_arg(argp,l_uacNumber)));
incr_top(L);
break;
}
case'p':{
char buff[4*sizeof(void*)+8];
sprintf(buff,"%p",va_arg(argp,void*));
pushstr(L,buff);
break;
}
case'%':{
pushstr(L,"%");
break;
}
default:{
char buff[3];
buff[0]='%';
buff[1]=*(e+1);
buff[2]='\0';
pushstr(L,buff);
break;
}
}
n+=2;
fmt=e+2;
}
pushstr(L,fmt);
luaV_concat(L,n+1,cast_int(L->top-L->base)-1);
L->top-=n;
return svalue(L->top-1);
}
static const char*luaO_pushfstring(lua_State*L,const char*fmt,...){
const char*msg;
va_list argp;
va_start(argp,fmt);
msg=luaO_pushvfstring(L,fmt,argp);
va_end(argp);
return msg;
}
static void luaO_chunkid(char*out,const char*source,size_t bufflen){
if(*source=='='){
strncpy(out,source+1,bufflen);
out[bufflen-1]='\0';
}
else{
if(*source=='@'){
size_t l;
source++;
bufflen-=sizeof(" '...' ");
l=strlen(source);
strcpy(out,"");
if(l>bufflen){
source+=(l-bufflen);
strcat(out,"...");
}
strcat(out,source);
}
else{
size_t len=strcspn(source,"\n\r");
bufflen-=sizeof(" [string \"...\"] ");
if(len>bufflen)len=bufflen;
strcpy(out,"[string \"");
if(source[len]!='\0'){
strncat(out,source,len);
strcat(out,"...");
}
else
strcat(out,source);
strcat(out,"\"]");
}
}
}
#define gnode(t,i)(&(t)->node[i])
#define gkey(n)(&(n)->i_key.nk)
#define gval(n)(&(n)->i_val)
#define gnext(n)((n)->i_key.nk.next)
#define key2tval(n)(&(n)->i_key.tvk)
static TValue*luaH_setnum(lua_State*L,Table*t,int key);
static const TValue*luaH_getstr(Table*t,TString*key);
static TValue*luaH_set(lua_State*L,Table*t,const TValue*key);
static const char*const luaT_typenames[]={
"nil","boolean","userdata","number",
"string","table","function","userdata","thread",
"proto","upval"
};
static void luaT_init(lua_State*L){
static const char*const luaT_eventname[]={
"__index","__newindex",
"__gc","__mode","__eq",
"__add","__sub","__mul","__div","__mod",
"__pow","__unm","__len","__lt","__le",
"__concat","__call"
};
int i;
for(i=0;i<TM_N;i++){
G(L)->tmname[i]=luaS_new(L,luaT_eventname[i]);
luaS_fix(G(L)->tmname[i]);
}
}
static const TValue*luaT_gettm(Table*events,TMS event,TString*ename){
const TValue*tm=luaH_getstr(events,ename);
if(ttisnil(tm)){
events->flags|=cast_byte(1u<<event);
return NULL;
}
else return tm;
}
static const TValue*luaT_gettmbyobj(lua_State*L,const TValue*o,TMS event){
Table*mt;
switch(ttype(o)){
case 5:
mt=hvalue(o)->metatable;
break;
case 7:
mt=uvalue(o)->metatable;
break;
default:
mt=G(L)->mt[ttype(o)];
}
return(mt?luaH_getstr(mt,G(L)->tmname[event]):(&luaO_nilobject_));
}
#define sizeCclosure(n)(cast(int,sizeof(CClosure))+cast(int,sizeof(TValue)*((n)-1)))
#define sizeLclosure(n)(cast(int,sizeof(LClosure))+cast(int,sizeof(TValue*)*((n)-1)))
static Closure*luaF_newCclosure(lua_State*L,int nelems,Table*e){
Closure*c=cast(Closure*,luaM_malloc(L,sizeCclosure(nelems)));
luaC_link(L,obj2gco(c),6);
c->c.isC=1;
c->c.env=e;
c->c.nupvalues=cast_byte(nelems);
return c;
}
static Closure*luaF_newLclosure(lua_State*L,int nelems,Table*e){
Closure*c=cast(Closure*,luaM_malloc(L,sizeLclosure(nelems)));
luaC_link(L,obj2gco(c),6);
c->l.isC=0;
c->l.env=e;
c->l.nupvalues=cast_byte(nelems);
while(nelems--)c->l.upvals[nelems]=NULL;
return c;
}
static UpVal*luaF_newupval(lua_State*L){
UpVal*uv=luaM_new(L,UpVal);
luaC_link(L,obj2gco(uv),(8+2));
uv->v=&uv->u.value;
setnilvalue(uv->v);
return uv;
}
static UpVal*luaF_findupval(lua_State*L,StkId level){
global_State*g=G(L);
GCObject**pp=&L->openupval;
UpVal*p;
UpVal*uv;
while(*pp!=NULL&&(p=ngcotouv(*pp))->v>=level){
if(p->v==level){
if(isdead(g,obj2gco(p)))
changewhite(obj2gco(p));
return p;
}
pp=&p->next;
}
uv=luaM_new(L,UpVal);
uv->tt=(8+2);
uv->marked=luaC_white(g);
uv->v=level;
uv->next=*pp;
*pp=obj2gco(uv);
uv->u.l.prev=&g->uvhead;
uv->u.l.next=g->uvhead.u.l.next;
uv->u.l.next->u.l.prev=uv;
g->uvhead.u.l.next=uv;
return uv;
}
static void unlinkupval(UpVal*uv){
uv->u.l.next->u.l.prev=uv->u.l.prev;
uv->u.l.prev->u.l.next=uv->u.l.next;
}
static void luaF_freeupval(lua_State*L,UpVal*uv){
if(uv->v!=&uv->u.value)
unlinkupval(uv);
luaM_free(L,uv);
}
static void luaF_close(lua_State*L,StkId level){
UpVal*uv;
global_State*g=G(L);
while(L->openupval!=NULL&&(uv=ngcotouv(L->openupval))->v>=level){
GCObject*o=obj2gco(uv);
L->openupval=uv->next;
if(isdead(g,o))
luaF_freeupval(L,uv);
else{
unlinkupval(uv);
setobj(L,&uv->u.value,uv->v);
uv->v=&uv->u.value;
luaC_linkupval(L,uv);
}
}
}
static Proto*luaF_newproto(lua_State*L){
Proto*f=luaM_new(L,Proto);
luaC_link(L,obj2gco(f),(8+1));
f->k=NULL;
f->sizek=0;
f->p=NULL;
f->sizep=0;
f->code=NULL;
f->sizecode=0;
f->sizelineinfo=0;
f->sizeupvalues=0;
f->nups=0;
f->upvalues=NULL;
f->numparams=0;
f->is_vararg=0;
f->maxstacksize=0;
f->lineinfo=NULL;
f->sizelocvars=0;
f->locvars=NULL;
f->linedefined=0;
f->lastlinedefined=0;
f->source=NULL;
return f;
}
static void luaF_freeproto(lua_State*L,Proto*f){
luaM_freearray(L,f->code,f->sizecode,Instruction);
luaM_freearray(L,f->p,f->sizep,Proto*);
luaM_freearray(L,f->k,f->sizek,TValue);
luaM_freearray(L,f->lineinfo,f->sizelineinfo,int);
luaM_freearray(L,f->locvars,f->sizelocvars,struct LocVar);
luaM_freearray(L,f->upvalues,f->sizeupvalues,TString*);
luaM_free(L,f);
}
static void luaF_freeclosure(lua_State*L,Closure*c){
int size=(c->c.isC)?sizeCclosure(c->c.nupvalues):
sizeLclosure(c->l.nupvalues);
luaM_freemem(L,c,size);
}
#define MASK1(n,p)((~((~(Instruction)0)<<n))<<p)
#define MASK0(n,p)(~MASK1(n,p))
#define GET_OPCODE(i)(cast(OpCode,((i)>>0)&MASK1(6,0)))
#define SET_OPCODE(i,o)((i)=(((i)&MASK0(6,0))|((cast(Instruction,o)<<0)&MASK1(6,0))))
#define GETARG_A(i)(cast(int,((i)>>(0+6))&MASK1(8,0)))
#define SETARG_A(i,u)((i)=(((i)&MASK0(8,(0+6)))|((cast(Instruction,u)<<(0+6))&MASK1(8,(0+6)))))
#define GETARG_B(i)(cast(int,((i)>>(((0+6)+8)+9))&MASK1(9,0)))
#define SETARG_B(i,b)((i)=(((i)&MASK0(9,(((0+6)+8)+9)))|((cast(Instruction,b)<<(((0+6)+8)+9))&MASK1(9,(((0+6)+8)+9)))))
#define GETARG_C(i)(cast(int,((i)>>((0+6)+8))&MASK1(9,0)))
#define SETARG_C(i,b)((i)=(((i)&MASK0(9,((0+6)+8)))|((cast(Instruction,b)<<((0+6)+8))&MASK1(9,((0+6)+8)))))
#define GETARG_Bx(i)(cast(int,((i)>>((0+6)+8))&MASK1((9+9),0)))
#define SETARG_Bx(i,b)((i)=(((i)&MASK0((9+9),((0+6)+8)))|((cast(Instruction,b)<<((0+6)+8))&MASK1((9+9),((0+6)+8)))))
#define GETARG_sBx(i)(GETARG_Bx(i)-(((1<<(9+9))-1)>>1))
#define SETARG_sBx(i,b)SETARG_Bx((i),cast(unsigned int,(b)+(((1<<(9+9))-1)>>1)))
#define CREATE_ABC(o,a,b,c)((cast(Instruction,o)<<0)|(cast(Instruction,a)<<(0+6))|(cast(Instruction,b)<<(((0+6)+8)+9))|(cast(Instruction,c)<<((0+6)+8)))
#define CREATE_ABx(o,a,bc)((cast(Instruction,o)<<0)|(cast(Instruction,a)<<(0+6))|(cast(Instruction,bc)<<((0+6)+8)))
#define ISK(x)((x)&(1<<(9-1)))
#define INDEXK(r)((int)(r)&~(1<<(9-1)))
#define RKASK(x)((x)|(1<<(9-1)))
static const lu_byte luaP_opmodes[(cast(int,OP_VARARG)+1)];
#define getBMode(m)(cast(enum OpArgMask,(luaP_opmodes[m]>>4)&3))
#define getCMode(m)(cast(enum OpArgMask,(luaP_opmodes[m]>>2)&3))
#define testTMode(m)(luaP_opmodes[m]&(1<<7))
typedef struct expdesc{
expkind k;
union{
struct{int info,aux;}s;
lua_Number nval;
}u;
int t;
int f;
}expdesc;
typedef struct upvaldesc{
lu_byte k;
lu_byte info;
}upvaldesc;
struct BlockCnt;
typedef struct FuncState{
Proto*f;
Table*h;
struct FuncState*prev;
struct LexState*ls;
struct lua_State*L;
struct BlockCnt*bl;
int pc;
int lasttarget;
int jpc;
int freereg;
int nk;
int np;
short nlocvars;
lu_byte nactvar;
upvaldesc upvalues[60];
unsigned short actvar[200];
}FuncState;
static Proto*luaY_parser(lua_State*L,ZIO*z,Mbuffer*buff,
const char*name);
struct lua_longjmp{
struct lua_longjmp*previous;
jmp_buf b;
volatile int status;
};
static void luaD_seterrorobj(lua_State*L,int errcode,StkId oldtop){
switch(errcode){
case 4:{
setsvalue(L,oldtop,luaS_newliteral(L,"not enough memory"));
break;
}
case 5:{
setsvalue(L,oldtop,luaS_newliteral(L,"error in error handling"));
break;
}
case 3:
case 2:{
setobj(L,oldtop,L->top-1);
break;
}
}
L->top=oldtop+1;
}
static void restore_stack_limit(lua_State*L){
if(L->size_ci>20000){
int inuse=cast_int(L->ci-L->base_ci);
if(inuse+1<20000)
luaD_reallocCI(L,20000);
}
}
static void resetstack(lua_State*L,int status){
L->ci=L->base_ci;
L->base=L->ci->base;
luaF_close(L,L->base);
luaD_seterrorobj(L,status,L->base);
L->nCcalls=L->baseCcalls;
L->allowhook=1;
restore_stack_limit(L);
L->errfunc=0;
L->errorJmp=NULL;
}
static void luaD_throw(lua_State*L,int errcode){
if(L->errorJmp){
L->errorJmp->status=errcode;
LUAI_THROW(L,L->errorJmp);
}
else{
L->status=cast_byte(errcode);
if(G(L)->panic){
resetstack(L,errcode);
G(L)->panic(L);
}
exit(EXIT_FAILURE);
}
}
static int luaD_rawrunprotected(lua_State*L,Pfunc f,void*ud){
struct lua_longjmp lj;
lj.status=0;
lj.previous=L->errorJmp;
L->errorJmp=&lj;
LUAI_TRY(L,&lj,
(*f)(L,ud);
);
L->errorJmp=lj.previous;
return lj.status;
}
static void correctstack(lua_State*L,TValue*oldstack){
CallInfo*ci;
GCObject*up;
L->top=(L->top-oldstack)+L->stack;
for(up=L->openupval;up!=NULL;up=up->gch.next)
gco2uv(up)->v=(gco2uv(up)->v-oldstack)+L->stack;
for(ci=L->base_ci;ci<=L->ci;ci++){
ci->top=(ci->top-oldstack)+L->stack;
ci->base=(ci->base-oldstack)+L->stack;
ci->func=(ci->func-oldstack)+L->stack;
}
L->base=(L->base-oldstack)+L->stack;
}
static void luaD_reallocstack(lua_State*L,int newsize){
TValue*oldstack=L->stack;
int realsize=newsize+1+5;
luaM_reallocvector(L,L->stack,L->stacksize,realsize,TValue);
L->stacksize=realsize;
L->stack_last=L->stack+newsize;
correctstack(L,oldstack);
}
static void luaD_reallocCI(lua_State*L,int newsize){
CallInfo*oldci=L->base_ci;
luaM_reallocvector(L,L->base_ci,L->size_ci,newsize,CallInfo);
L->size_ci=newsize;
L->ci=(L->ci-oldci)+L->base_ci;
L->end_ci=L->base_ci+L->size_ci-1;
}
static void luaD_growstack(lua_State*L,int n){
if(n<=L->stacksize)
luaD_reallocstack(L,2*L->stacksize);
else
luaD_reallocstack(L,L->stacksize+n);
}
static CallInfo*growCI(lua_State*L){
if(L->size_ci>20000)
luaD_throw(L,5);
else{
luaD_reallocCI(L,2*L->size_ci);
if(L->size_ci>20000)
luaG_runerror(L,"stack overflow");
}
return++L->ci;
}
static StkId adjust_varargs(lua_State*L,Proto*p,int actual){
int i;
int nfixargs=p->numparams;
Table*htab=NULL;
StkId base,fixed;
for(;actual<nfixargs;++actual)
setnilvalue(L->top++);
fixed=L->top-actual;
base=L->top;
for(i=0;i<nfixargs;i++){
setobj(L,L->top++,fixed+i);
setnilvalue(fixed+i);
}
if(htab){
sethvalue(L,L->top++,htab);
}
return base;
}
static StkId tryfuncTM(lua_State*L,StkId func){
const TValue*tm=luaT_gettmbyobj(L,func,TM_CALL);
StkId p;
ptrdiff_t funcr=savestack(L,func);
if(!ttisfunction(tm))
luaG_typeerror(L,func,"call");
for(p=L->top;p>func;p--)setobj(L,p,p-1);
incr_top(L);
func=restorestack(L,funcr);
setobj(L,func,tm);
return func;
}
#define inc_ci(L)((L->ci==L->end_ci)?growCI(L):(condhardstacktests(luaD_reallocCI(L,L->size_ci)),++L->ci))
static int luaD_precall(lua_State*L,StkId func,int nresults){
LClosure*cl;
ptrdiff_t funcr;
if(!ttisfunction(func))
func=tryfuncTM(L,func);
funcr=savestack(L,func);
cl=&clvalue(func)->l;
L->ci->savedpc=L->savedpc;
if(!cl->isC){
CallInfo*ci;
StkId st,base;
Proto*p=cl->p;
luaD_checkstack(L,p->maxstacksize+p->numparams);
func=restorestack(L,funcr);
if(!p->is_vararg){
base=func+1;
if(L->top>base+p->numparams)
L->top=base+p->numparams;
}
else{
int nargs=cast_int(L->top-func)-1;
base=adjust_varargs(L,p,nargs);
func=restorestack(L,funcr);
}
ci=inc_ci(L);
ci->func=func;
L->base=ci->base=base;
ci->top=L->base+p->maxstacksize;
L->savedpc=p->code;
ci->tailcalls=0;
ci->nresults=nresults;
for(st=L->top;st<ci->top;st++)
setnilvalue(st);
L->top=ci->top;
return 0;
}
else{
CallInfo*ci;
int n;
luaD_checkstack(L,20);
ci=inc_ci(L);
ci->func=restorestack(L,funcr);
L->base=ci->base=ci->func+1;
ci->top=L->top+20;
ci->nresults=nresults;
n=(*curr_func(L)->c.f)(L);
if(n<0)
return 2;
else{
luaD_poscall(L,L->top-n);
return 1;
}
}
}
static int luaD_poscall(lua_State*L,StkId firstResult){
StkId res;
int wanted,i;
CallInfo*ci;
ci=L->ci--;
res=ci->func;
wanted=ci->nresults;
L->base=(ci-1)->base;
L->savedpc=(ci-1)->savedpc;
for(i=wanted;i!=0&&firstResult<L->top;i--)
setobj(L,res++,firstResult++);
while(i-->0)
setnilvalue(res++);
L->top=res;
return(wanted-(-1));
}
static void luaD_call(lua_State*L,StkId func,int nResults){
if(++L->nCcalls>=200){
if(L->nCcalls==200)
luaG_runerror(L,"C stack overflow");
else if(L->nCcalls>=(200+(200>>3)))
luaD_throw(L,5);
}
if(luaD_precall(L,func,nResults)==0)
luaV_execute(L,1);
L->nCcalls--;
luaC_checkGC(L);
}
static int luaD_pcall(lua_State*L,Pfunc func,void*u,
ptrdiff_t old_top,ptrdiff_t ef){
int status;
unsigned short oldnCcalls=L->nCcalls;
ptrdiff_t old_ci=saveci(L,L->ci);
lu_byte old_allowhooks=L->allowhook;
ptrdiff_t old_errfunc=L->errfunc;
L->errfunc=ef;
status=luaD_rawrunprotected(L,func,u);
if(status!=0){
StkId oldtop=restorestack(L,old_top);
luaF_close(L,oldtop);
luaD_seterrorobj(L,status,oldtop);
L->nCcalls=oldnCcalls;
L->ci=restoreci(L,old_ci);
L->base=L->ci->base;
L->savedpc=L->ci->savedpc;
L->allowhook=old_allowhooks;
restore_stack_limit(L);
}
L->errfunc=old_errfunc;
return status;
}
struct SParser{
ZIO*z;
Mbuffer buff;
const char*name;
};
static void f_parser(lua_State*L,void*ud){
int i;
Proto*tf;
Closure*cl;
struct SParser*p=cast(struct SParser*,ud);
luaC_checkGC(L);
tf=luaY_parser(L,p->z,
&p->buff,p->name);
cl=luaF_newLclosure(L,tf->nups,hvalue(gt(L)));
cl->l.p=tf;
for(i=0;i<tf->nups;i++)
cl->l.upvals[i]=luaF_newupval(L);
setclvalue(L,L->top,cl);
incr_top(L);
}
static int luaD_protectedparser(lua_State*L,ZIO*z,const char*name){
struct SParser p;
int status;
p.z=z;p.name=name;
luaZ_initbuffer(L,&p.buff);
status=luaD_pcall(L,f_parser,&p,savestack(L,L->top),L->errfunc);
luaZ_freebuffer(L,&p.buff);
return status;
}
static void luaS_resize(lua_State*L,int newsize){
GCObject**newhash;
stringtable*tb;
int i;
if(G(L)->gcstate==2)
return;
newhash=luaM_newvector(L,newsize,GCObject*);
tb=&G(L)->strt;
for(i=0;i<newsize;i++)newhash[i]=NULL;
for(i=0;i<tb->size;i++){
GCObject*p=tb->hash[i];
while(p){
GCObject*next=p->gch.next;
unsigned int h=gco2ts(p)->hash;
int h1=lmod(h,newsize);
p->gch.next=newhash[h1];
newhash[h1]=p;
p=next;
}
}
luaM_freearray(L,tb->hash,tb->size,TString*);
tb->size=newsize;
tb->hash=newhash;
}
static TString*newlstr(lua_State*L,const char*str,size_t l,
unsigned int h){
TString*ts;
stringtable*tb;
if(l+1>(((size_t)(~(size_t)0)-2)-sizeof(TString))/sizeof(char))
luaM_toobig(L);
ts=cast(TString*,luaM_malloc(L,(l+1)*sizeof(char)+sizeof(TString)));
ts->tsv.len=l;
ts->tsv.hash=h;
ts->tsv.marked=luaC_white(G(L));
ts->tsv.tt=4;
ts->tsv.reserved=0;
memcpy(ts+1,str,l*sizeof(char));
((char*)(ts+1))[l]='\0';
tb=&G(L)->strt;
h=lmod(h,tb->size);
ts->tsv.next=tb->hash[h];
tb->hash[h]=obj2gco(ts);
tb->nuse++;
if(tb->nuse>cast(lu_int32,tb->size)&&tb->size<=(INT_MAX-2)/2)
luaS_resize(L,tb->size*2);
return ts;
}
static TString*luaS_newlstr(lua_State*L,const char*str,size_t l){
GCObject*o;
unsigned int h=cast(unsigned int,l);
size_t step=(l>>5)+1;
size_t l1;
for(l1=l;l1>=step;l1-=step)
h=h^((h<<5)+(h>>2)+cast(unsigned char,str[l1-1]));
for(o=G(L)->strt.hash[lmod(h,G(L)->strt.size)];
o!=NULL;
o=o->gch.next){
TString*ts=rawgco2ts(o);
if(ts->tsv.len==l&&(memcmp(str,getstr(ts),l)==0)){
if(isdead(G(L),o))changewhite(o);
return ts;
}
}
return newlstr(L,str,l,h);
}
static Udata*luaS_newudata(lua_State*L,size_t s,Table*e){
Udata*u;
if(s>((size_t)(~(size_t)0)-2)-sizeof(Udata))
luaM_toobig(L);
u=cast(Udata*,luaM_malloc(L,s+sizeof(Udata)));
u->uv.marked=luaC_white(G(L));
u->uv.tt=7;
u->uv.len=s;
u->uv.metatable=NULL;
u->uv.env=e;
u->uv.next=G(L)->mainthread->next;
G(L)->mainthread->next=obj2gco(u);
return u;
}
#define hashpow2(t,n)(gnode(t,lmod((n),sizenode(t))))
#define hashstr(t,str)hashpow2(t,(str)->tsv.hash)
#define hashboolean(t,p)hashpow2(t,p)
#define hashmod(t,n)(gnode(t,((n)%((sizenode(t)-1)|1))))
#define hashpointer(t,p)hashmod(t,IntPoint(p))
static const Node dummynode_={
{{NULL},0},
{{{NULL},0,NULL}}
};
static Node*hashnum(const Table*t,lua_Number n){
unsigned int a[cast_int(sizeof(lua_Number)/sizeof(int))];
int i;
if(luai_numeq(n,0))
return gnode(t,0);
memcpy(a,&n,sizeof(a));
for(i=1;i<cast_int(sizeof(lua_Number)/sizeof(int));i++)a[0]+=a[i];
return hashmod(t,a[0]);
}
static Node*mainposition(const Table*t,const TValue*key){
switch(ttype(key)){
case 3:
return hashnum(t,nvalue(key));
case 4:
return hashstr(t,rawtsvalue(key));
case 1:
return hashboolean(t,bvalue(key));
case 2:
return hashpointer(t,pvalue(key));
default:
return hashpointer(t,gcvalue(key));
}
}
static int arrayindex(const TValue*key){
if(ttisnumber(key)){
lua_Number n=nvalue(key);
int k;
lua_number2int(k,n);
if(luai_numeq(cast_num(k),n))
return k;
}
return-1;
}
static int findindex(lua_State*L,Table*t,StkId key){
int i;
if(ttisnil(key))return-1;
i=arrayindex(key);
if(0<i&&i<=t->sizearray)
return i-1;
else{
Node*n=mainposition(t,key);
do{
if(luaO_rawequalObj(key2tval(n),key)||
(ttype(gkey(n))==(8+3)&&iscollectable(key)&&
gcvalue(gkey(n))==gcvalue(key))){
i=cast_int(n-gnode(t,0));
return i+t->sizearray;
}
else n=gnext(n);
}while(n);
luaG_runerror(L,"invalid key to "LUA_QL("next"));
return 0;
}
}
static int luaH_next(lua_State*L,Table*t,StkId key){
int i=findindex(L,t,key);
for(i++;i<t->sizearray;i++){
if(!ttisnil(&t->array[i])){
setnvalue(key,cast_num(i+1));
setobj(L,key+1,&t->array[i]);
return 1;
}
}
for(i-=t->sizearray;i<(int)sizenode(t);i++){
if(!ttisnil(gval(gnode(t,i)))){
setobj(L,key,key2tval(gnode(t,i)));
setobj(L,key+1,gval(gnode(t,i)));
return 1;
}
}
return 0;
}
static int computesizes(int nums[],int*narray){
int i;
int twotoi;
int a=0;
int na=0;
int n=0;
for(i=0,twotoi=1;twotoi/2<*narray;i++,twotoi*=2){
if(nums[i]>0){
a+=nums[i];
if(a>twotoi/2){
n=twotoi;
na=a;
}
}
if(a==*narray)break;
}
*narray=n;
return na;
}
static int countint(const TValue*key,int*nums){
int k=arrayindex(key);
if(0<k&&k<=(1<<(32-2))){
nums[ceillog2(k)]++;
return 1;
}
else
return 0;
}
static int numusearray(const Table*t,int*nums){
int lg;
int ttlg;
int ause=0;
int i=1;
for(lg=0,ttlg=1;lg<=(32-2);lg++,ttlg*=2){
int lc=0;
int lim=ttlg;
if(lim>t->sizearray){
lim=t->sizearray;
if(i>lim)
break;
}
for(;i<=lim;i++){
if(!ttisnil(&t->array[i-1]))
lc++;
}
nums[lg]+=lc;
ause+=lc;
}
return ause;
}
static int numusehash(const Table*t,int*nums,int*pnasize){
int totaluse=0;
int ause=0;
int i=sizenode(t);
while(i--){
Node*n=&t->node[i];
if(!ttisnil(gval(n))){
ause+=countint(key2tval(n),nums);
totaluse++;
}
}
*pnasize+=ause;
return totaluse;
}
static void setarrayvector(lua_State*L,Table*t,int size){
int i;
luaM_reallocvector(L,t->array,t->sizearray,size,TValue);
for(i=t->sizearray;i<size;i++)
setnilvalue(&t->array[i]);
t->sizearray=size;
}
static void setnodevector(lua_State*L,Table*t,int size){
int lsize;
if(size==0){
t->node=cast(Node*,(&dummynode_));
lsize=0;
}
else{
int i;
lsize=ceillog2(size);
if(lsize>(32-2))
luaG_runerror(L,"table overflow");
size=twoto(lsize);
t->node=luaM_newvector(L,size,Node);
for(i=0;i<size;i++){
Node*n=gnode(t,i);
gnext(n)=NULL;
setnilvalue(gkey(n));
setnilvalue(gval(n));
}
}
t->lsizenode=cast_byte(lsize);
t->lastfree=gnode(t,size);
}
static void resize(lua_State*L,Table*t,int nasize,int nhsize){
int i;
int oldasize=t->sizearray;
int oldhsize=t->lsizenode;
Node*nold=t->node;
if(nasize>oldasize)
setarrayvector(L,t,nasize);
setnodevector(L,t,nhsize);
if(nasize<oldasize){
t->sizearray=nasize;
for(i=nasize;i<oldasize;i++){
if(!ttisnil(&t->array[i]))
setobj(L,luaH_setnum(L,t,i+1),&t->array[i]);
}
luaM_reallocvector(L,t->array,oldasize,nasize,TValue);
}
for(i=twoto(oldhsize)-1;i>=0;i--){
Node*old=nold+i;
if(!ttisnil(gval(old)))
setobj(L,luaH_set(L,t,key2tval(old)),gval(old));
}
if(nold!=(&dummynode_))
luaM_freearray(L,nold,twoto(oldhsize),Node);
}
static void luaH_resizearray(lua_State*L,Table*t,int nasize){
int nsize=(t->node==(&dummynode_))?0:sizenode(t);
resize(L,t,nasize,nsize);
}
static void rehash(lua_State*L,Table*t,const TValue*ek){
int nasize,na;
int nums[(32-2)+1];
int i;
int totaluse;
for(i=0;i<=(32-2);i++)nums[i]=0;
nasize=numusearray(t,nums);
totaluse=nasize;
totaluse+=numusehash(t,nums,&nasize);
nasize+=countint(ek,nums);
totaluse++;
na=computesizes(nums,&nasize);
resize(L,t,nasize,totaluse-na);
}
static Table*luaH_new(lua_State*L,int narray,int nhash){
Table*t=luaM_new(L,Table);
luaC_link(L,obj2gco(t),5);
t->metatable=NULL;
t->flags=cast_byte(~0);
t->array=NULL;
t->sizearray=0;
t->lsizenode=0;
t->node=cast(Node*,(&dummynode_));
setarrayvector(L,t,narray);
setnodevector(L,t,nhash);
return t;
}
static void luaH_free(lua_State*L,Table*t){
if(t->node!=(&dummynode_))
luaM_freearray(L,t->node,sizenode(t),Node);
luaM_freearray(L,t->array,t->sizearray,TValue);
luaM_free(L,t);
}
static Node*getfreepos(Table*t){
while(t->lastfree-->t->node){
if(ttisnil(gkey(t->lastfree)))
return t->lastfree;
}
return NULL;
}
static TValue*newkey(lua_State*L,Table*t,const TValue*key){
Node*mp=mainposition(t,key);
if(!ttisnil(gval(mp))||mp==(&dummynode_)){
Node*othern;
Node*n=getfreepos(t);
if(n==NULL){
rehash(L,t,key);
return luaH_set(L,t,key);
}
othern=mainposition(t,key2tval(mp));
if(othern!=mp){
while(gnext(othern)!=mp)othern=gnext(othern);
gnext(othern)=n;
*n=*mp;
gnext(mp)=NULL;
setnilvalue(gval(mp));
}
else{
gnext(n)=gnext(mp);
gnext(mp)=n;
mp=n;
}
}
gkey(mp)->value=key->value;gkey(mp)->tt=key->tt;
luaC_barriert(L,t,key);
return gval(mp);
}
static const TValue*luaH_getnum(Table*t,int key){
if(cast(unsigned int,key)-1<cast(unsigned int,t->sizearray))
return&t->array[key-1];
else{
lua_Number nk=cast_num(key);
Node*n=hashnum(t,nk);
do{
if(ttisnumber(gkey(n))&&luai_numeq(nvalue(gkey(n)),nk))
return gval(n);
else n=gnext(n);
}while(n);
return(&luaO_nilobject_);
}
}
static const TValue*luaH_getstr(Table*t,TString*key){
Node*n=hashstr(t,key);
do{
if(ttisstring(gkey(n))&&rawtsvalue(gkey(n))==key)
return gval(n);
else n=gnext(n);
}while(n);
return(&luaO_nilobject_);
}
static const TValue*luaH_get(Table*t,const TValue*key){
switch(ttype(key)){
case 0:return(&luaO_nilobject_);
case 4:return luaH_getstr(t,rawtsvalue(key));
case 3:{
int k;
lua_Number n=nvalue(key);
lua_number2int(k,n);
if(luai_numeq(cast_num(k),nvalue(key)))
return luaH_getnum(t,k);
}
/*fallthrough*/
default:{
Node*n=mainposition(t,key);
do{
if(luaO_rawequalObj(key2tval(n),key))
return gval(n);
else n=gnext(n);
}while(n);
return(&luaO_nilobject_);
}
}
}
static TValue*luaH_set(lua_State*L,Table*t,const TValue*key){
const TValue*p=luaH_get(t,key);
t->flags=0;
if(p!=(&luaO_nilobject_))
return cast(TValue*,p);
else{
if(ttisnil(key))luaG_runerror(L,"table index is nil");
else if(ttisnumber(key)&&luai_numisnan(nvalue(key)))
luaG_runerror(L,"table index is NaN");
return newkey(L,t,key);
}
}
static TValue*luaH_setnum(lua_State*L,Table*t,int key){
const TValue*p=luaH_getnum(t,key);
if(p!=(&luaO_nilobject_))
return cast(TValue*,p);
else{
TValue k;
setnvalue(&k,cast_num(key));
return newkey(L,t,&k);
}
}
static TValue*luaH_setstr(lua_State*L,Table*t,TString*key){
const TValue*p=luaH_getstr(t,key);
if(p!=(&luaO_nilobject_))
return cast(TValue*,p);
else{
TValue k;
setsvalue(L,&k,key);
return newkey(L,t,&k);
}
}
static int unbound_search(Table*t,unsigned int j){
unsigned int i=j;
j++;
while(!ttisnil(luaH_getnum(t,j))){
i=j;
j*=2;
if(j>cast(unsigned int,(INT_MAX-2))){
i=1;
while(!ttisnil(luaH_getnum(t,i)))i++;
return i-1;
}
}
while(j-i>1){
unsigned int m=(i+j)/2;
if(ttisnil(luaH_getnum(t,m)))j=m;
else i=m;
}
return i;
}
static int luaH_getn(Table*t){
unsigned int j=t->sizearray;
if(j>0&&ttisnil(&t->array[j-1])){
unsigned int i=0;
while(j-i>1){
unsigned int m=(i+j)/2;
if(ttisnil(&t->array[m-1]))j=m;
else i=m;
}
return i;
}
else if(t->node==(&dummynode_))
return j;
else return unbound_search(t,j);
}
#define makewhite(g,x)((x)->gch.marked=cast_byte(((x)->gch.marked&cast_byte(~(bitmask(2)|bit2mask(0,1))))|luaC_white(g)))
#define white2gray(x)reset2bits((x)->gch.marked,0,1)
#define black2gray(x)resetbit((x)->gch.marked,2)
#define stringmark(s)reset2bits((s)->tsv.marked,0,1)
#define isfinalized(u)testbit((u)->marked,3)
#define markfinalized(u)l_setbit((u)->marked,3)
#define markvalue(g,o){checkconsistency(o);if(iscollectable(o)&&iswhite(gcvalue(o)))reallymarkobject(g,gcvalue(o));}
#define markobject(g,t){if(iswhite(obj2gco(t)))reallymarkobject(g,obj2gco(t));}
#define setthreshold(g)(g->GCthreshold=(g->estimate/100)*g->gcpause)
static void removeentry(Node*n){
if(iscollectable(gkey(n)))
setttype(gkey(n),(8+3));
}
static void reallymarkobject(global_State*g,GCObject*o){
white2gray(o);
switch(o->gch.tt){
case 4:{
return;
}
case 7:{
Table*mt=gco2u(o)->metatable;
gray2black(o);
if(mt)markobject(g,mt);
markobject(g,gco2u(o)->env);
return;
}
case(8+2):{
UpVal*uv=gco2uv(o);
markvalue(g,uv->v);
if(uv->v==&uv->u.value)
gray2black(o);
return;
}
case 6:{
gco2cl(o)->c.gclist=g->gray;
g->gray=o;
break;
}
case 5:{
gco2h(o)->gclist=g->gray;
g->gray=o;
break;
}
case 8:{
gco2th(o)->gclist=g->gray;
g->gray=o;
break;
}
case(8+1):{
gco2p(o)->gclist=g->gray;
g->gray=o;
break;
}
default:;
}
}
static void marktmu(global_State*g){
GCObject*u=g->tmudata;
if(u){
do{
u=u->gch.next;
makewhite(g,u);
reallymarkobject(g,u);
}while(u!=g->tmudata);
}
}
static size_t luaC_separateudata(lua_State*L,int all){
global_State*g=G(L);
size_t deadmem=0;
GCObject**p=&g->mainthread->next;
GCObject*curr;
while((curr=*p)!=NULL){
if(!(iswhite(curr)||all)||isfinalized(gco2u(curr)))
p=&curr->gch.next;
else if(fasttm(L,gco2u(curr)->metatable,TM_GC)==NULL){
markfinalized(gco2u(curr));
p=&curr->gch.next;
}
else{
deadmem+=sizeudata(gco2u(curr));
markfinalized(gco2u(curr));
*p=curr->gch.next;
if(g->tmudata==NULL)
g->tmudata=curr->gch.next=curr;
else{
curr->gch.next=g->tmudata->gch.next;
g->tmudata->gch.next=curr;
g->tmudata=curr;
}
}
}
return deadmem;
}
static int traversetable(global_State*g,Table*h){
int i;
int weakkey=0;
int weakvalue=0;
const TValue*mode;
if(h->metatable)
markobject(g,h->metatable);
mode=gfasttm(g,h->metatable,TM_MODE);
if(mode&&ttisstring(mode)){
weakkey=(strchr(svalue(mode),'k')!=NULL);
weakvalue=(strchr(svalue(mode),'v')!=NULL);
if(weakkey||weakvalue){
h->marked&=~(bitmask(3)|bitmask(4));
h->marked|=cast_byte((weakkey<<3)|
(weakvalue<<4));
h->gclist=g->weak;
g->weak=obj2gco(h);
}
}
if(weakkey&&weakvalue)return 1;
if(!weakvalue){
i=h->sizearray;
while(i--)
markvalue(g,&h->array[i]);
}
i=sizenode(h);
while(i--){
Node*n=gnode(h,i);
if(ttisnil(gval(n)))
removeentry(n);
else{
if(!weakkey)markvalue(g,gkey(n));
if(!weakvalue)markvalue(g,gval(n));
}
}
return weakkey||weakvalue;
}
static void traverseproto(global_State*g,Proto*f){
int i;
if(f->source)stringmark(f->source);
for(i=0;i<f->sizek;i++)
markvalue(g,&f->k[i]);
for(i=0;i<f->sizeupvalues;i++){
if(f->upvalues[i])
stringmark(f->upvalues[i]);
}
for(i=0;i<f->sizep;i++){
if(f->p[i])
markobject(g,f->p[i]);
}
for(i=0;i<f->sizelocvars;i++){
if(f->locvars[i].varname)
stringmark(f->locvars[i].varname);
}
}
static void traverseclosure(global_State*g,Closure*cl){
markobject(g,cl->c.env);
if(cl->c.isC){
int i;
for(i=0;i<cl->c.nupvalues;i++)
markvalue(g,&cl->c.upvalue[i]);
}
else{
int i;
markobject(g,cl->l.p);
for(i=0;i<cl->l.nupvalues;i++)
markobject(g,cl->l.upvals[i]);
}
}
static void checkstacksizes(lua_State*L,StkId max){
int ci_used=cast_int(L->ci-L->base_ci);
int s_used=cast_int(max-L->stack);
if(L->size_ci>20000)
return;
if(4*ci_used<L->size_ci&&2*8<L->size_ci)
luaD_reallocCI(L,L->size_ci/2);
condhardstacktests(luaD_reallocCI(L,ci_used+1));
if(4*s_used<L->stacksize&&
2*((2*20)+5)<L->stacksize)
luaD_reallocstack(L,L->stacksize/2);
condhardstacktests(luaD_reallocstack(L,s_used));
}
static void traversestack(global_State*g,lua_State*l){
StkId o,lim;
CallInfo*ci;
markvalue(g,gt(l));
lim=l->top;
for(ci=l->base_ci;ci<=l->ci;ci++){
if(lim<ci->top)lim=ci->top;
}
for(o=l->stack;o<l->top;o++)
markvalue(g,o);
for(;o<=lim;o++)
setnilvalue(o);
checkstacksizes(l,lim);
}
static l_mem propagatemark(global_State*g){
GCObject*o=g->gray;
gray2black(o);
switch(o->gch.tt){
case 5:{
Table*h=gco2h(o);
g->gray=h->gclist;
if(traversetable(g,h))
black2gray(o);
return sizeof(Table)+sizeof(TValue)*h->sizearray+
sizeof(Node)*sizenode(h);
}
case 6:{
Closure*cl=gco2cl(o);
g->gray=cl->c.gclist;
traverseclosure(g,cl);
return(cl->c.isC)?sizeCclosure(cl->c.nupvalues):
sizeLclosure(cl->l.nupvalues);
}
case 8:{
lua_State*th=gco2th(o);
g->gray=th->gclist;
th->gclist=g->grayagain;
g->grayagain=o;
black2gray(o);
traversestack(g,th);
return sizeof(lua_State)+sizeof(TValue)*th->stacksize+
sizeof(CallInfo)*th->size_ci;
}
case(8+1):{
Proto*p=gco2p(o);
g->gray=p->gclist;
traverseproto(g,p);
return sizeof(Proto)+sizeof(Instruction)*p->sizecode+
sizeof(Proto*)*p->sizep+
sizeof(TValue)*p->sizek+
sizeof(int)*p->sizelineinfo+
sizeof(LocVar)*p->sizelocvars+
sizeof(TString*)*p->sizeupvalues;
}
default:return 0;
}
}
static size_t propagateall(global_State*g){
size_t m=0;
while(g->gray)m+=propagatemark(g);
return m;
}
static int iscleared(const TValue*o,int iskey){
if(!iscollectable(o))return 0;
if(ttisstring(o)){
stringmark(rawtsvalue(o));
return 0;
}
return iswhite(gcvalue(o))||
(ttisuserdata(o)&&(!iskey&&isfinalized(uvalue(o))));
}
static void cleartable(GCObject*l){
while(l){
Table*h=gco2h(l);
int i=h->sizearray;
if(testbit(h->marked,4)){
while(i--){
TValue*o=&h->array[i];
if(iscleared(o,0))
setnilvalue(o);
}
}
i=sizenode(h);
while(i--){
Node*n=gnode(h,i);
if(!ttisnil(gval(n))&&
(iscleared(key2tval(n),1)||iscleared(gval(n),0))){
setnilvalue(gval(n));
removeentry(n);
}
}
l=h->gclist;
}
}
static void freeobj(lua_State*L,GCObject*o){
switch(o->gch.tt){
case(8+1):luaF_freeproto(L,gco2p(o));break;
case 6:luaF_freeclosure(L,gco2cl(o));break;
case(8+2):luaF_freeupval(L,gco2uv(o));break;
case 5:luaH_free(L,gco2h(o));break;
case 8:{
luaE_freethread(L,gco2th(o));
break;
}
case 4:{
G(L)->strt.nuse--;
luaM_freemem(L,o,sizestring(gco2ts(o)));
break;
}
case 7:{
luaM_freemem(L,o,sizeudata(gco2u(o)));
break;
}
default:;
}
}
#define sweepwholelist(L,p)sweeplist(L,p,((lu_mem)(~(lu_mem)0)-2))
static GCObject**sweeplist(lua_State*L,GCObject**p,lu_mem count){
GCObject*curr;
global_State*g=G(L);
int deadmask=otherwhite(g);
while((curr=*p)!=NULL&&count-->0){
if(curr->gch.tt==8)
sweepwholelist(L,&gco2th(curr)->openupval);
if((curr->gch.marked^bit2mask(0,1))&deadmask){
makewhite(g,curr);
p=&curr->gch.next;
}
else{
*p=curr->gch.next;
if(curr==g->rootgc)
g->rootgc=curr->gch.next;
freeobj(L,curr);
}
}
return p;
}
static void checkSizes(lua_State*L){
global_State*g=G(L);
if(g->strt.nuse<cast(lu_int32,g->strt.size/4)&&
g->strt.size>32*2)
luaS_resize(L,g->strt.size/2);
if(luaZ_sizebuffer(&g->buff)>32*2){
size_t newsize=luaZ_sizebuffer(&g->buff)/2;
luaZ_resizebuffer(L,&g->buff,newsize);
}
}
static void GCTM(lua_State*L){
global_State*g=G(L);
GCObject*o=g->tmudata->gch.next;
Udata*udata=rawgco2u(o);
const TValue*tm;
if(o==g->tmudata)
g->tmudata=NULL;
else
g->tmudata->gch.next=udata->uv.next;
udata->uv.next=g->mainthread->next;
g->mainthread->next=o;
makewhite(g,o);
tm=fasttm(L,udata->uv.metatable,TM_GC);
if(tm!=NULL){
lu_byte oldah=L->allowhook;
lu_mem oldt=g->GCthreshold;
L->allowhook=0;
g->GCthreshold=2*g->totalbytes;
setobj(L,L->top,tm);
setuvalue(L,L->top+1,udata);
L->top+=2;
luaD_call(L,L->top-2,0);
L->allowhook=oldah;
g->GCthreshold=oldt;
}
}
static void luaC_callGCTM(lua_State*L){
while(G(L)->tmudata)
GCTM(L);
}
static void luaC_freeall(lua_State*L){
global_State*g=G(L);
int i;
g->currentwhite=bit2mask(0,1)|bitmask(6);
sweepwholelist(L,&g->rootgc);
for(i=0;i<g->strt.size;i++)
sweepwholelist(L,&g->strt.hash[i]);
}
static void markmt(global_State*g){
int i;
for(i=0;i<(8+1);i++)
if(g->mt[i])markobject(g,g->mt[i]);
}
static void markroot(lua_State*L){
global_State*g=G(L);
g->gray=NULL;
g->grayagain=NULL;
g->weak=NULL;
markobject(g,g->mainthread);
markvalue(g,gt(g->mainthread));
markvalue(g,registry(L));
markmt(g);
g->gcstate=1;
}
static void remarkupvals(global_State*g){
UpVal*uv;
for(uv=g->uvhead.u.l.next;uv!=&g->uvhead;uv=uv->u.l.next){
if(isgray(obj2gco(uv)))
markvalue(g,uv->v);
}
}
static void atomic(lua_State*L){
global_State*g=G(L);
size_t udsize;
remarkupvals(g);
propagateall(g);
g->gray=g->weak;
g->weak=NULL;
markobject(g,L);
markmt(g);
propagateall(g);
g->gray=g->grayagain;
g->grayagain=NULL;
propagateall(g);
udsize=luaC_separateudata(L,0);
marktmu(g);
udsize+=propagateall(g);
cleartable(g->weak);
g->currentwhite=cast_byte(otherwhite(g));
g->sweepstrgc=0;
g->sweepgc=&g->rootgc;
g->gcstate=2;
g->estimate=g->totalbytes-udsize;
}
static l_mem singlestep(lua_State*L){
global_State*g=G(L);
switch(g->gcstate){
case 0:{
markroot(L);
return 0;
}
case 1:{
if(g->gray)
return propagatemark(g);
else{
atomic(L);
return 0;
}
}
case 2:{
lu_mem old=g->totalbytes;
sweepwholelist(L,&g->strt.hash[g->sweepstrgc++]);
if(g->sweepstrgc>=g->strt.size)
g->gcstate=3;
g->estimate-=old-g->totalbytes;
return 10;
}
case 3:{
lu_mem old=g->totalbytes;
g->sweepgc=sweeplist(L,g->sweepgc,40);
if(*g->sweepgc==NULL){
checkSizes(L);
g->gcstate=4;
}
g->estimate-=old-g->totalbytes;
return 40*10;
}
case 4:{
if(g->tmudata){
GCTM(L);
if(g->estimate>100)
g->estimate-=100;
return 100;
}
else{
g->gcstate=0;
g->gcdept=0;
return 0;
}
}
default:return 0;
}
}
static void luaC_step(lua_State*L){
global_State*g=G(L);
l_mem lim=(1024u/100)*g->gcstepmul;
if(lim==0)
lim=(((lu_mem)(~(lu_mem)0)-2)-1)/2;
g->gcdept+=g->totalbytes-g->GCthreshold;
do{
lim-=singlestep(L);
if(g->gcstate==0)
break;
}while(lim>0);
if(g->gcstate!=0){
if(g->gcdept<1024u)
g->GCthreshold=g->totalbytes+1024u;
else{
g->gcdept-=1024u;
g->GCthreshold=g->totalbytes;
}
}
else{
setthreshold(g);
}
}
static void luaC_barrierf(lua_State*L,GCObject*o,GCObject*v){
global_State*g=G(L);
if(g->gcstate==1)
reallymarkobject(g,v);
else
makewhite(g,o);
}
static void luaC_barrierback(lua_State*L,Table*t){
global_State*g=G(L);
GCObject*o=obj2gco(t);
black2gray(o);
t->gclist=g->grayagain;
g->grayagain=o;
}
static void luaC_link(lua_State*L,GCObject*o,lu_byte tt){
global_State*g=G(L);
o->gch.next=g->rootgc;
g->rootgc=o;
o->gch.marked=luaC_white(g);
o->gch.tt=tt;
}
static void luaC_linkupval(lua_State*L,UpVal*uv){
global_State*g=G(L);
GCObject*o=obj2gco(uv);
o->gch.next=g->rootgc;
g->rootgc=o;
if(isgray(o)){
if(g->gcstate==1){
gray2black(o);
luaC_barrier(L,uv,uv->v);
}
else{
makewhite(g,o);
}
}
}
typedef union{
lua_Number r;
TString*ts;
}SemInfo;
typedef struct Token{
int token;
SemInfo seminfo;
}Token;
typedef struct LexState{
int current;
int linenumber;
int lastline;
Token t;
Token lookahead;
struct FuncState*fs;
struct lua_State*L;
ZIO*z;
Mbuffer*buff;
TString*source;
char decpoint;
}LexState;
static void luaX_init(lua_State*L);
static void luaX_lexerror(LexState*ls,const char*msg,int token);
#define state_size(x)(sizeof(x)+0)
#define fromstate(l)(cast(lu_byte*,(l))-0)
#define tostate(l)(cast(lua_State*,cast(lu_byte*,l)+0))
typedef struct LG{
lua_State l;
global_State g;
}LG;
static void stack_init(lua_State*L1,lua_State*L){
L1->base_ci=luaM_newvector(L,8,CallInfo);
L1->ci=L1->base_ci;
L1->size_ci=8;
L1->end_ci=L1->base_ci+L1->size_ci-1;
L1->stack=luaM_newvector(L,(2*20)+5,TValue);
L1->stacksize=(2*20)+5;
L1->top=L1->stack;
L1->stack_last=L1->stack+(L1->stacksize-5)-1;
L1->ci->func=L1->top;
setnilvalue(L1->top++);
L1->base=L1->ci->base=L1->top;
L1->ci->top=L1->top+20;
}
static void freestack(lua_State*L,lua_State*L1){
luaM_freearray(L,L1->base_ci,L1->size_ci,CallInfo);
luaM_freearray(L,L1->stack,L1->stacksize,TValue);
}
static void f_luaopen(lua_State*L,void*ud){
global_State*g=G(L);
UNUSED(ud);
stack_init(L,L);
sethvalue(L,gt(L),luaH_new(L,0,2));
sethvalue(L,registry(L),luaH_new(L,0,2));
luaS_resize(L,32);
luaT_init(L);
luaX_init(L);
luaS_fix(luaS_newliteral(L,"not enough memory"));
g->GCthreshold=4*g->totalbytes;
}
static void preinit_state(lua_State*L,global_State*g){
G(L)=g;
L->stack=NULL;
L->stacksize=0;
L->errorJmp=NULL;
L->hook=NULL;
L->hookmask=0;
L->basehookcount=0;
L->allowhook=1;
resethookcount(L);
L->openupval=NULL;
L->size_ci=0;
L->nCcalls=L->baseCcalls=0;
L->status=0;
L->base_ci=L->ci=NULL;
L->savedpc=NULL;
L->errfunc=0;
setnilvalue(gt(L));
}
static void close_state(lua_State*L){
global_State*g=G(L);
luaF_close(L,L->stack);
luaC_freeall(L);
luaM_freearray(L,G(L)->strt.hash,G(L)->strt.size,TString*);
luaZ_freebuffer(L,&g->buff);
freestack(L,L);
(*g->frealloc)(g->ud,fromstate(L),state_size(LG),0);
}
static void luaE_freethread(lua_State*L,lua_State*L1){
luaF_close(L1,L1->stack);
freestack(L,L1);
luaM_freemem(L,fromstate(L1),state_size(lua_State));
}
static lua_State*lua_newstate(lua_Alloc f,void*ud){
int i;
lua_State*L;
global_State*g;
void*l=(*f)(ud,NULL,0,state_size(LG));
if(l==NULL)return NULL;
L=tostate(l);
g=&((LG*)L)->g;
L->next=NULL;
L->tt=8;
g->currentwhite=bit2mask(0,5);
L->marked=luaC_white(g);
set2bits(L->marked,5,6);
preinit_state(L,g);
g->frealloc=f;
g->ud=ud;
g->mainthread=L;
g->uvhead.u.l.prev=&g->uvhead;
g->uvhead.u.l.next=&g->uvhead;
g->GCthreshold=0;
g->strt.size=0;
g->strt.nuse=0;
g->strt.hash=NULL;
setnilvalue(registry(L));
luaZ_initbuffer(L,&g->buff);
g->panic=NULL;
g->gcstate=0;
g->rootgc=obj2gco(L);
g->sweepstrgc=0;
g->sweepgc=&g->rootgc;
g->gray=NULL;
g->grayagain=NULL;
g->weak=NULL;
g->tmudata=NULL;
g->totalbytes=sizeof(LG);
g->gcpause=200;
g->gcstepmul=200;
g->gcdept=0;
for(i=0;i<(8+1);i++)g->mt[i]=NULL;
if(luaD_rawrunprotected(L,f_luaopen,NULL)!=0){
close_state(L);
L=NULL;
}
else
{}
return L;
}
static void callallgcTM(lua_State*L,void*ud){
UNUSED(ud);
luaC_callGCTM(L);
}
static void lua_close(lua_State*L){
L=G(L)->mainthread;
luaF_close(L,L->stack);
luaC_separateudata(L,1);
L->errfunc=0;
do{
L->ci=L->base_ci;
L->base=L->top=L->ci->base;
L->nCcalls=L->baseCcalls=0;
}while(luaD_rawrunprotected(L,callallgcTM,NULL)!=0);
close_state(L);
}
#define getcode(fs,e)((fs)->f->code[(e)->u.s.info])
#define luaK_codeAsBx(fs,o,A,sBx)luaK_codeABx(fs,o,A,(sBx)+(((1<<(9+9))-1)>>1))
#define luaK_setmultret(fs,e)luaK_setreturns(fs,e,(-1))
static int luaK_codeABx(FuncState*fs,OpCode o,int A,unsigned int Bx);
static int luaK_codeABC(FuncState*fs,OpCode o,int A,int B,int C);
static void luaK_setreturns(FuncState*fs,expdesc*e,int nresults);
static void luaK_patchtohere(FuncState*fs,int list);
static void luaK_concat(FuncState*fs,int*l1,int l2);
static int currentpc(lua_State*L,CallInfo*ci){
if(!isLua(ci))return-1;
if(ci==L->ci)
ci->savedpc=L->savedpc;
return pcRel(ci->savedpc,ci_func(ci)->l.p);
}
static int currentline(lua_State*L,CallInfo*ci){
int pc=currentpc(L,ci);
if(pc<0)
return-1;
else
return getline_(ci_func(ci)->l.p,pc);
}
static int lua_getstack(lua_State*L,int level,lua_Debug*ar){
int status;
CallInfo*ci;
for(ci=L->ci;level>0&&ci>L->base_ci;ci--){
level--;
if(f_isLua(ci))
level-=ci->tailcalls;
}
if(level==0&&ci>L->base_ci){
status=1;
ar->i_ci=cast_int(ci-L->base_ci);
}
else if(level<0){
status=1;
ar->i_ci=0;
}
else status=0;
return status;
}
static Proto*getluaproto(CallInfo*ci){
return(isLua(ci)?ci_func(ci)->l.p:NULL);
}
static void funcinfo(lua_Debug*ar,Closure*cl){
if(cl->c.isC){
ar->source="=[C]";
ar->linedefined=-1;
ar->lastlinedefined=-1;
ar->what="C";
}
else{
ar->source=getstr(cl->l.p->source);
ar->linedefined=cl->l.p->linedefined;
ar->lastlinedefined=cl->l.p->lastlinedefined;
ar->what=(ar->linedefined==0)?"main":"Lua";
}
luaO_chunkid(ar->short_src,ar->source,60);
}
static void info_tailcall(lua_Debug*ar){
ar->name=ar->namewhat="";
ar->what="tail";
ar->lastlinedefined=ar->linedefined=ar->currentline=-1;
ar->source="=(tail call)";
luaO_chunkid(ar->short_src,ar->source,60);
ar->nups=0;
}
static void collectvalidlines(lua_State*L,Closure*f){
if(f==NULL||f->c.isC){
setnilvalue(L->top);
}
else{
Table*t=luaH_new(L,0,0);
int*lineinfo=f->l.p->lineinfo;
int i;
for(i=0;i<f->l.p->sizelineinfo;i++)
setbvalue(luaH_setnum(L,t,lineinfo[i]),1);
sethvalue(L,L->top,t);
}
incr_top(L);
}
static int auxgetinfo(lua_State*L,const char*what,lua_Debug*ar,
Closure*f,CallInfo*ci){
int status=1;
if(f==NULL){
info_tailcall(ar);
return status;
}
for(;*what;what++){
switch(*what){
case'S':{
funcinfo(ar,f);
break;
}
case'l':{
ar->currentline=(ci)?currentline(L,ci):-1;
break;
}
case'u':{
ar->nups=f->c.nupvalues;
break;
}
case'n':{
ar->namewhat=(ci)?NULL:NULL;
if(ar->namewhat==NULL){
ar->namewhat="";
ar->name=NULL;
}
break;
}
case'L':
case'f':
break;
default:status=0;
}
}
return status;
}
static int lua_getinfo(lua_State*L,const char*what,lua_Debug*ar){
int status;
Closure*f=NULL;
CallInfo*ci=NULL;
if(*what=='>'){
StkId func=L->top-1;
luai_apicheck(L,ttisfunction(func));
what++;
f=clvalue(func);
L->top--;
}
else if(ar->i_ci!=0){
ci=L->base_ci+ar->i_ci;
f=clvalue(ci->func);
}
status=auxgetinfo(L,what,ar,f,ci);
if(strchr(what,'f')){
if(f==NULL)setnilvalue(L->top);
else setclvalue(L,L->top,f);
incr_top(L);
}
if(strchr(what,'L'))
collectvalidlines(L,f);
return status;
}
static int isinstack(CallInfo*ci,const TValue*o){
StkId p;
for(p=ci->base;p<ci->top;p++)
if(o==p)return 1;
return 0;
}
static void luaG_typeerror(lua_State*L,const TValue*o,const char*op){
const char*name=NULL;
const char*t=luaT_typenames[ttype(o)];
const char*kind=(isinstack(L->ci,o))?
NULL:
NULL;
if(kind)
luaG_runerror(L,"attempt to %s %s "LUA_QL("%s")" (a %s value)",
op,kind,name,t);
else
luaG_runerror(L,"attempt to %s a %s value",op,t);
}
static void luaG_concaterror(lua_State*L,StkId p1,StkId p2){
if(ttisstring(p1)||ttisnumber(p1))p1=p2;
luaG_typeerror(L,p1,"concatenate");
}
static void luaG_aritherror(lua_State*L,const TValue*p1,const TValue*p2){
TValue temp;
if(luaV_tonumber(p1,&temp)==NULL)
p2=p1;
luaG_typeerror(L,p2,"perform arithmetic on");
}
static int luaG_ordererror(lua_State*L,const TValue*p1,const TValue*p2){
const char*t1=luaT_typenames[ttype(p1)];
const char*t2=luaT_typenames[ttype(p2)];
if(t1[2]==t2[2])
luaG_runerror(L,"attempt to compare two %s values",t1);
else
luaG_runerror(L,"attempt to compare %s with %s",t1,t2);
return 0;
}
static void addinfo(lua_State*L,const char*msg){
CallInfo*ci=L->ci;
if(isLua(ci)){
char buff[60];
int line=currentline(L,ci);
luaO_chunkid(buff,getstr(getluaproto(ci)->source),60);
luaO_pushfstring(L,"%s:%d: %s",buff,line,msg);
}
}
static void luaG_errormsg(lua_State*L){
if(L->errfunc!=0){
StkId errfunc=restorestack(L,L->errfunc);
if(!ttisfunction(errfunc))luaD_throw(L,5);
setobj(L,L->top,L->top-1);
setobj(L,L->top-1,errfunc);
incr_top(L);
luaD_call(L,L->top-2,1);
}
luaD_throw(L,2);
}
static void luaG_runerror(lua_State*L,const char*fmt,...){
va_list argp;
va_start(argp,fmt);
addinfo(L,luaO_pushvfstring(L,fmt,argp));
va_end(argp);
luaG_errormsg(L);
}
static int luaZ_fill(ZIO*z){
size_t size;
lua_State*L=z->L;
const char*buff;
buff=z->reader(L,z->data,&size);
if(buff==NULL||size==0)return(-1);
z->n=size-1;
z->p=buff;
return char2int(*(z->p++));
}
static void luaZ_init(lua_State*L,ZIO*z,lua_Reader reader,void*data){
z->L=L;
z->reader=reader;
z->data=data;
z->n=0;
z->p=NULL;
}
static char*luaZ_openspace(lua_State*L,Mbuffer*buff,size_t n){
if(n>buff->buffsize){
if(n<32)n=32;
luaZ_resizebuffer(L,buff,n);
}
return buff->buffer;
}
#define opmode(t,a,b,c,m)(((t)<<7)|((a)<<6)|((b)<<4)|((c)<<2)|(m))
static const lu_byte luaP_opmodes[(cast(int,OP_VARARG)+1)]={
opmode(0,1,OpArgR,OpArgN,iABC)
,opmode(0,1,OpArgK,OpArgN,iABx)
,opmode(0,1,OpArgU,OpArgU,iABC)
,opmode(0,1,OpArgR,OpArgN,iABC)
,opmode(0,1,OpArgU,OpArgN,iABC)
,opmode(0,1,OpArgK,OpArgN,iABx)
,opmode(0,1,OpArgR,OpArgK,iABC)
,opmode(0,0,OpArgK,OpArgN,iABx)
,opmode(0,0,OpArgU,OpArgN,iABC)
,opmode(0,0,OpArgK,OpArgK,iABC)
,opmode(0,1,OpArgU,OpArgU,iABC)
,opmode(0,1,OpArgR,OpArgK,iABC)
,opmode(0,1,OpArgK,OpArgK,iABC)
,opmode(0,1,OpArgK,OpArgK,iABC)
,opmode(0,1,OpArgK,OpArgK,iABC)
,opmode(0,1,OpArgK,OpArgK,iABC)
,opmode(0,1,OpArgK,OpArgK,iABC)
,opmode(0,1,OpArgK,OpArgK,iABC)
,opmode(0,1,OpArgR,OpArgN,iABC)
,opmode(0,1,OpArgR,OpArgN,iABC)
,opmode(0,1,OpArgR,OpArgN,iABC)
,opmode(0,1,OpArgR,OpArgR,iABC)
,opmode(0,0,OpArgR,OpArgN,iAsBx)
,opmode(1,0,OpArgK,OpArgK,iABC)
,opmode(1,0,OpArgK,OpArgK,iABC)
,opmode(1,0,OpArgK,OpArgK,iABC)
,opmode(1,1,OpArgR,OpArgU,iABC)
,opmode(1,1,OpArgR,OpArgU,iABC)
,opmode(0,1,OpArgU,OpArgU,iABC)
,opmode(0,1,OpArgU,OpArgU,iABC)
,opmode(0,0,OpArgU,OpArgN,iABC)
,opmode(0,1,OpArgR,OpArgN,iAsBx)
,opmode(0,1,OpArgR,OpArgN,iAsBx)
,opmode(1,0,OpArgN,OpArgU,iABC)
,opmode(0,0,OpArgU,OpArgU,iABC)
,opmode(0,0,OpArgN,OpArgN,iABC)
,opmode(0,1,OpArgU,OpArgN,iABx)
,opmode(0,1,OpArgU,OpArgN,iABC)
};
#define next(ls)(ls->current=zgetc(ls->z))
#define currIsNewline(ls)(ls->current=='\n'||ls->current=='\r')
static const char*const luaX_tokens[]={
"and","break","do","else","elseif",
"end","false","for","function","if",
"in","local","nil","not","or","repeat",
"return","then","true","until","while",
"..","...","==",">=","<=","~=",
"<number>","<name>","<string>","<eof>",
NULL
};
#define save_and_next(ls)(save(ls,ls->current),next(ls))
static void save(LexState*ls,int c){
Mbuffer*b=ls->buff;
if(b->n+1>b->buffsize){
size_t newsize;
if(b->buffsize>=((size_t)(~(size_t)0)-2)/2)
luaX_lexerror(ls,"lexical element too long",0);
newsize=b->buffsize*2;
luaZ_resizebuffer(ls->L,b,newsize);
}
b->buffer[b->n++]=cast(char,c);
}
static void luaX_init(lua_State*L){
int i;
for(i=0;i<(cast(int,TK_WHILE-257+1));i++){
TString*ts=luaS_new(L,luaX_tokens[i]);
luaS_fix(ts);
ts->tsv.reserved=cast_byte(i+1);
}
}
static const char*luaX_token2str(LexState*ls,int token){
if(token<257){
return(iscntrl(token))?luaO_pushfstring(ls->L,"char(%d)",token):
luaO_pushfstring(ls->L,"%c",token);
}
else
return luaX_tokens[token-257];
}
static const char*txtToken(LexState*ls,int token){
switch(token){
case TK_NAME:
case TK_STRING:
case TK_NUMBER:
save(ls,'\0');
return luaZ_buffer(ls->buff);
default:
return luaX_token2str(ls,token);
}
}
static void luaX_lexerror(LexState*ls,const char*msg,int token){
char buff[80];
luaO_chunkid(buff,getstr(ls->source),80);
msg=luaO_pushfstring(ls->L,"%s:%d: %s",buff,ls->linenumber,msg);
if(token)
luaO_pushfstring(ls->L,"%s near "LUA_QL("%s"),msg,txtToken(ls,token));
luaD_throw(ls->L,3);
}
static void luaX_syntaxerror(LexState*ls,const char*msg){
luaX_lexerror(ls,msg,ls->t.token);
}
static TString*luaX_newstring(LexState*ls,const char*str,size_t l){
lua_State*L=ls->L;
TString*ts=luaS_newlstr(L,str,l);
TValue*o=luaH_setstr(L,ls->fs->h,ts);
if(ttisnil(o)){
setbvalue(o,1);
luaC_checkGC(L);
}
return ts;
}
static void inclinenumber(LexState*ls){
int old=ls->current;
next(ls);
if(currIsNewline(ls)&&ls->current!=old)
next(ls);
if(++ls->linenumber>=(INT_MAX-2))
luaX_syntaxerror(ls,"chunk has too many lines");
}
static void luaX_setinput(lua_State*L,LexState*ls,ZIO*z,TString*source){
ls->decpoint='.';
ls->L=L;
ls->lookahead.token=TK_EOS;
ls->z=z;
ls->fs=NULL;
ls->linenumber=1;
ls->lastline=1;
ls->source=source;
luaZ_resizebuffer(ls->L,ls->buff,32);
next(ls);
}
static int check_next(LexState*ls,const char*set){
if(!strchr(set,ls->current))
return 0;
save_and_next(ls);
return 1;
}
static void buffreplace(LexState*ls,char from,char to){
size_t n=luaZ_bufflen(ls->buff);
char*p=luaZ_buffer(ls->buff);
while(n--)
if(p[n]==from)p[n]=to;
}
static void read_numeral(LexState*ls,SemInfo*seminfo){
do{
save_and_next(ls);
}while(isdigit(ls->current)||ls->current=='.');
if(check_next(ls,"Ee"))
check_next(ls,"+-");
while(isalnum(ls->current)||ls->current=='_')
save_and_next(ls);
save(ls,'\0');
buffreplace(ls,'.',ls->decpoint);
if(!luaO_str2d(luaZ_buffer(ls->buff),&seminfo->r))
luaX_lexerror(ls,"malformed number",TK_NUMBER);
}
static int skip_sep(LexState*ls){
int count=0;
int s=ls->current;
save_and_next(ls);
while(ls->current=='='){
save_and_next(ls);
count++;
}
return(ls->current==s)?count:(-count)-1;
}
static void read_long_string(LexState*ls,SemInfo*seminfo,int sep){
int cont=0;
(void)(cont);
save_and_next(ls);
if(currIsNewline(ls))
inclinenumber(ls);
for(;;){
switch(ls->current){
case(-1):
luaX_lexerror(ls,(seminfo)?"unfinished long string":
"unfinished long comment",TK_EOS);
break;
case']':{
if(skip_sep(ls)==sep){
save_and_next(ls);
goto endloop;
}
break;
}
case'\n':
case'\r':{
save(ls,'\n');
inclinenumber(ls);
if(!seminfo)luaZ_resetbuffer(ls->buff);
break;
}
default:{
if(seminfo)save_and_next(ls);
else next(ls);
}
}
}endloop:
if(seminfo)
seminfo->ts=luaX_newstring(ls,luaZ_buffer(ls->buff)+(2+sep),
luaZ_bufflen(ls->buff)-2*(2+sep));
}
static void read_string(LexState*ls,int del,SemInfo*seminfo){
save_and_next(ls);
while(ls->current!=del){
switch(ls->current){
case(-1):
luaX_lexerror(ls,"unfinished string",TK_EOS);
continue;
case'\n':
case'\r':
luaX_lexerror(ls,"unfinished string",TK_STRING);
continue;
case'\\':{
int c;
next(ls);
switch(ls->current){
case'a':c='\a';break;
case'b':c='\b';break;
case'f':c='\f';break;
case'n':c='\n';break;
case'r':c='\r';break;
case't':c='\t';break;
case'v':c='\v';break;
case'\n':
case'\r':save(ls,'\n');inclinenumber(ls);continue;
case(-1):continue;
default:{
if(!isdigit(ls->current))
save_and_next(ls);
else{
int i=0;
c=0;
do{
c=10*c+(ls->current-'0');
next(ls);
}while(++i<3&&isdigit(ls->current));
if(c>UCHAR_MAX)
luaX_lexerror(ls,"escape sequence too large",TK_STRING);
save(ls,c);
}
continue;
}
}
save(ls,c);
next(ls);
continue;
}
default:
save_and_next(ls);
}
}
save_and_next(ls);
seminfo->ts=luaX_newstring(ls,luaZ_buffer(ls->buff)+1,
luaZ_bufflen(ls->buff)-2);
}
static int llex(LexState*ls,SemInfo*seminfo){
luaZ_resetbuffer(ls->buff);
for(;;){
switch(ls->current){
case'\n':
case'\r':{
inclinenumber(ls);
continue;
}
case'-':{
next(ls);
if(ls->current!='-')return'-';
next(ls);
if(ls->current=='['){
int sep=skip_sep(ls);
luaZ_resetbuffer(ls->buff);
if(sep>=0){
read_long_string(ls,NULL,sep);
luaZ_resetbuffer(ls->buff);
continue;
}
}
while(!currIsNewline(ls)&&ls->current!=(-1))
next(ls);
continue;
}
case'[':{
int sep=skip_sep(ls);
if(sep>=0){
read_long_string(ls,seminfo,sep);
return TK_STRING;
}
else if (sep!=-1)luaX_lexerror(ls,"invalid long string delimiter",TK_STRING);
return'[';
}
case'=':{
next(ls);
if(ls->current!='=')return'=';
else{next(ls);return TK_EQ;}
}
case'<':{
next(ls);
if(ls->current!='=')return'<';
else{next(ls);return TK_LE;}
}
case'>':{
next(ls);
if(ls->current!='=')return'>';
else{next(ls);return TK_GE;}
}
case'~':{
next(ls);
if(ls->current!='=')return'~';
else{next(ls);return TK_NE;}
}
case'"':
case'\'':{
read_string(ls,ls->current,seminfo);
return TK_STRING;
}
case'.':{
save_and_next(ls);
if(check_next(ls,".")){
if(check_next(ls,"."))
return TK_DOTS;
else return TK_CONCAT;
}
else if(!isdigit(ls->current))return'.';
else{
read_numeral(ls,seminfo);
return TK_NUMBER;
}
}
case(-1):{
return TK_EOS;
}
default:{
if(isspace(ls->current)){
next(ls);
continue;
}
else if(isdigit(ls->current)){
read_numeral(ls,seminfo);
return TK_NUMBER;
}
else if(isalpha(ls->current)||ls->current=='_'){
TString*ts;
do{
save_and_next(ls);
}while(isalnum(ls->current)||ls->current=='_');
ts=luaX_newstring(ls,luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
if(ts->tsv.reserved>0)
return ts->tsv.reserved-1+257;
else{
seminfo->ts=ts;
return TK_NAME;
}
}
else{
int c=ls->current;
next(ls);
return c;
}
}
}
}
}
static void luaX_next(LexState*ls){
ls->lastline=ls->linenumber;
if(ls->lookahead.token!=TK_EOS){
ls->t=ls->lookahead;
ls->lookahead.token=TK_EOS;
}
else
ls->t.token=llex(ls,&ls->t.seminfo);
}
static void luaX_lookahead(LexState*ls){
ls->lookahead.token=llex(ls,&ls->lookahead.seminfo);
}
#define hasjumps(e)((e)->t!=(e)->f)
static int isnumeral(expdesc*e){
return(e->k==VKNUM&&e->t==(-1)&&e->f==(-1));
}
static void luaK_nil(FuncState*fs,int from,int n){
Instruction*previous;
if(fs->pc>fs->lasttarget){
if(fs->pc==0){
if(from>=fs->nactvar)
return;
}
else{
previous=&fs->f->code[fs->pc-1];
if(GET_OPCODE(*previous)==OP_LOADNIL){
int pfrom=GETARG_A(*previous);
int pto=GETARG_B(*previous);
if(pfrom<=from&&from<=pto+1){
if(from+n-1>pto)
SETARG_B(*previous,from+n-1);
return;
}
}
}
}
luaK_codeABC(fs,OP_LOADNIL,from,from+n-1,0);
}
static int luaK_jump(FuncState*fs){
int jpc=fs->jpc;
int j;
fs->jpc=(-1);
j=luaK_codeAsBx(fs,OP_JMP,0,(-1));
luaK_concat(fs,&j,jpc);
return j;
}
static void luaK_ret(FuncState*fs,int first,int nret){
luaK_codeABC(fs,OP_RETURN,first,nret+1,0);
}
static int condjump(FuncState*fs,OpCode op,int A,int B,int C){
luaK_codeABC(fs,op,A,B,C);
return luaK_jump(fs);
}
static void fixjump(FuncState*fs,int pc,int dest){
Instruction*jmp=&fs->f->code[pc];
int offset=dest-(pc+1);
if(abs(offset)>(((1<<(9+9))-1)>>1))
luaX_syntaxerror(fs->ls,"control structure too long");
SETARG_sBx(*jmp,offset);
}
static int luaK_getlabel(FuncState*fs){
fs->lasttarget=fs->pc;
return fs->pc;
}
static int getjump(FuncState*fs,int pc){
int offset=GETARG_sBx(fs->f->code[pc]);
if(offset==(-1))
return(-1);
else
return(pc+1)+offset;
}
static Instruction*getjumpcontrol(FuncState*fs,int pc){
Instruction*pi=&fs->f->code[pc];
if(pc>=1&&testTMode(GET_OPCODE(*(pi-1))))
return pi-1;
else
return pi;
}
static int need_value(FuncState*fs,int list){
for(;list!=(-1);list=getjump(fs,list)){
Instruction i=*getjumpcontrol(fs,list);
if(GET_OPCODE(i)!=OP_TESTSET)return 1;
}
return 0;
}
static int patchtestreg(FuncState*fs,int node,int reg){
Instruction*i=getjumpcontrol(fs,node);
if(GET_OPCODE(*i)!=OP_TESTSET)
return 0;
if(reg!=((1<<8)-1)&&reg!=GETARG_B(*i))
SETARG_A(*i,reg);
else
*i=CREATE_ABC(OP_TEST,GETARG_B(*i),0,GETARG_C(*i));
return 1;
}
static void removevalues(FuncState*fs,int list){
for(;list!=(-1);list=getjump(fs,list))
patchtestreg(fs,list,((1<<8)-1));
}
static void patchlistaux(FuncState*fs,int list,int vtarget,int reg,
int dtarget){
while(list!=(-1)){
int next=getjump(fs,list);
if(patchtestreg(fs,list,reg))
fixjump(fs,list,vtarget);
else
fixjump(fs,list,dtarget);
list=next;
}
}
static void dischargejpc(FuncState*fs){
patchlistaux(fs,fs->jpc,fs->pc,((1<<8)-1),fs->pc);
fs->jpc=(-1);
}
static void luaK_patchlist(FuncState*fs,int list,int target){
if(target==fs->pc)
luaK_patchtohere(fs,list);
else{
patchlistaux(fs,list,target,((1<<8)-1),target);
}
}
static void luaK_patchtohere(FuncState*fs,int list){
luaK_getlabel(fs);
luaK_concat(fs,&fs->jpc,list);
}
static void luaK_concat(FuncState*fs,int*l1,int l2){
if(l2==(-1))return;
else if(*l1==(-1))
*l1=l2;
else{
int list=*l1;
int next;
while((next=getjump(fs,list))!=(-1))
list=next;
fixjump(fs,list,l2);
}
}
static void luaK_checkstack(FuncState*fs,int n){
int newstack=fs->freereg+n;
if(newstack>fs->f->maxstacksize){
if(newstack>=250)
luaX_syntaxerror(fs->ls,"function or expression too complex");
fs->f->maxstacksize=cast_byte(newstack);
}
}
static void luaK_reserveregs(FuncState*fs,int n){
luaK_checkstack(fs,n);
fs->freereg+=n;
}
static void freereg(FuncState*fs,int reg){
if(!ISK(reg)&&reg>=fs->nactvar){
fs->freereg--;
}
}
static void freeexp(FuncState*fs,expdesc*e){
if(e->k==VNONRELOC)
freereg(fs,e->u.s.info);
}
static int addk(FuncState*fs,TValue*k,TValue*v){
lua_State*L=fs->L;
TValue*idx=luaH_set(L,fs->h,k);
Proto*f=fs->f;
int oldsize=f->sizek;
if(ttisnumber(idx)){
return cast_int(nvalue(idx));
}
else{
setnvalue(idx,cast_num(fs->nk));
luaM_growvector(L,f->k,fs->nk,f->sizek,TValue,
((1<<(9+9))-1),"constant table overflow");
while(oldsize<f->sizek)setnilvalue(&f->k[oldsize++]);
setobj(L,&f->k[fs->nk],v);
luaC_barrier(L,f,v);
return fs->nk++;
}
}
static int luaK_stringK(FuncState*fs,TString*s){
TValue o;
setsvalue(fs->L,&o,s);
return addk(fs,&o,&o);
}
static int luaK_numberK(FuncState*fs,lua_Number r){
TValue o;
setnvalue(&o,r);
return addk(fs,&o,&o);
}
static int boolK(FuncState*fs,int b){
TValue o;
setbvalue(&o,b);
return addk(fs,&o,&o);
}
static int nilK(FuncState*fs){
TValue k,v;
setnilvalue(&v);
sethvalue(fs->L,&k,fs->h);
return addk(fs,&k,&v);
}
static void luaK_setreturns(FuncState*fs,expdesc*e,int nresults){
if(e->k==VCALL){
SETARG_C(getcode(fs,e),nresults+1);
}
else if(e->k==VVARARG){
SETARG_B(getcode(fs,e),nresults+1);
SETARG_A(getcode(fs,e),fs->freereg);
luaK_reserveregs(fs,1);
}
}
static void luaK_setoneret(FuncState*fs,expdesc*e){
if(e->k==VCALL){
e->k=VNONRELOC;
e->u.s.info=GETARG_A(getcode(fs,e));
}
else if(e->k==VVARARG){
SETARG_B(getcode(fs,e),2);
e->k=VRELOCABLE;
}
}
static void luaK_dischargevars(FuncState*fs,expdesc*e){
switch(e->k){
case VLOCAL:{
e->k=VNONRELOC;
break;
}
case VUPVAL:{
e->u.s.info=luaK_codeABC(fs,OP_GETUPVAL,0,e->u.s.info,0);
e->k=VRELOCABLE;
break;
}
case VGLOBAL:{
e->u.s.info=luaK_codeABx(fs,OP_GETGLOBAL,0,e->u.s.info);
e->k=VRELOCABLE;
break;
}
case VINDEXED:{
freereg(fs,e->u.s.aux);
freereg(fs,e->u.s.info);
e->u.s.info=luaK_codeABC(fs,OP_GETTABLE,0,e->u.s.info,e->u.s.aux);
e->k=VRELOCABLE;
break;
}
case VVARARG:
case VCALL:{
luaK_setoneret(fs,e);
break;
}
default:break;
}
}
static int code_label(FuncState*fs,int A,int b,int jump){
luaK_getlabel(fs);
return luaK_codeABC(fs,OP_LOADBOOL,A,b,jump);
}
static void discharge2reg(FuncState*fs,expdesc*e,int reg){
luaK_dischargevars(fs,e);
switch(e->k){
case VNIL:{
luaK_nil(fs,reg,1);
break;
}
case VFALSE:case VTRUE:{
luaK_codeABC(fs,OP_LOADBOOL,reg,e->k==VTRUE,0);
break;
}
case VK:{
luaK_codeABx(fs,OP_LOADK,reg,e->u.s.info);
break;
}
case VKNUM:{
luaK_codeABx(fs,OP_LOADK,reg,luaK_numberK(fs,e->u.nval));
break;
}
case VRELOCABLE:{
Instruction*pc=&getcode(fs,e);
SETARG_A(*pc,reg);
break;
}
case VNONRELOC:{
if(reg!=e->u.s.info)
luaK_codeABC(fs,OP_MOVE,reg,e->u.s.info,0);
break;
}
default:{
return;
}
}
e->u.s.info=reg;
e->k=VNONRELOC;
}
static void discharge2anyreg(FuncState*fs,expdesc*e){
if(e->k!=VNONRELOC){
luaK_reserveregs(fs,1);
discharge2reg(fs,e,fs->freereg-1);
}
}
static void exp2reg(FuncState*fs,expdesc*e,int reg){
discharge2reg(fs,e,reg);
if(e->k==VJMP)
luaK_concat(fs,&e->t,e->u.s.info);
if(hasjumps(e)){
int final;
int p_f=(-1);
int p_t=(-1);
if(need_value(fs,e->t)||need_value(fs,e->f)){
int fj=(e->k==VJMP)?(-1):luaK_jump(fs);
p_f=code_label(fs,reg,0,1);
p_t=code_label(fs,reg,1,0);
luaK_patchtohere(fs,fj);
}
final=luaK_getlabel(fs);
patchlistaux(fs,e->f,final,reg,p_f);
patchlistaux(fs,e->t,final,reg,p_t);
}
e->f=e->t=(-1);
e->u.s.info=reg;
e->k=VNONRELOC;
}
static void luaK_exp2nextreg(FuncState*fs,expdesc*e){
luaK_dischargevars(fs,e);
freeexp(fs,e);
luaK_reserveregs(fs,1);
exp2reg(fs,e,fs->freereg-1);
}
static int luaK_exp2anyreg(FuncState*fs,expdesc*e){
luaK_dischargevars(fs,e);
if(e->k==VNONRELOC){
if(!hasjumps(e))return e->u.s.info;
if(e->u.s.info>=fs->nactvar){
exp2reg(fs,e,e->u.s.info);
return e->u.s.info;
}
}
luaK_exp2nextreg(fs,e);
return e->u.s.info;
}
static void luaK_exp2val(FuncState*fs,expdesc*e){
if(hasjumps(e))
luaK_exp2anyreg(fs,e);
else
luaK_dischargevars(fs,e);
}
static int luaK_exp2RK(FuncState*fs,expdesc*e){
luaK_exp2val(fs,e);
switch(e->k){
case VKNUM:
case VTRUE:
case VFALSE:
case VNIL:{
if(fs->nk<=((1<<(9-1))-1)){
e->u.s.info=(e->k==VNIL)?nilK(fs):
(e->k==VKNUM)?luaK_numberK(fs,e->u.nval):
boolK(fs,(e->k==VTRUE));
e->k=VK;
return RKASK(e->u.s.info);
}
else break;
}
case VK:{
if(e->u.s.info<=((1<<(9-1))-1))
return RKASK(e->u.s.info);
else break;
}
default:break;
}
return luaK_exp2anyreg(fs,e);
}
static void luaK_storevar(FuncState*fs,expdesc*var,expdesc*ex){
switch(var->k){
case VLOCAL:{
freeexp(fs,ex);
exp2reg(fs,ex,var->u.s.info);
return;
}
case VUPVAL:{
int e=luaK_exp2anyreg(fs,ex);
luaK_codeABC(fs,OP_SETUPVAL,e,var->u.s.info,0);
break;
}
case VGLOBAL:{
int e=luaK_exp2anyreg(fs,ex);
luaK_codeABx(fs,OP_SETGLOBAL,e,var->u.s.info);
break;
}
case VINDEXED:{
int e=luaK_exp2RK(fs,ex);
luaK_codeABC(fs,OP_SETTABLE,var->u.s.info,var->u.s.aux,e);
break;
}
default:{
break;
}
}
freeexp(fs,ex);
}
static void luaK_self(FuncState*fs,expdesc*e,expdesc*key){
int func;
luaK_exp2anyreg(fs,e);
freeexp(fs,e);
func=fs->freereg;
luaK_reserveregs(fs,2);
luaK_codeABC(fs,OP_SELF,func,e->u.s.info,luaK_exp2RK(fs,key));
freeexp(fs,key);
e->u.s.info=func;
e->k=VNONRELOC;
}
static void invertjump(FuncState*fs,expdesc*e){
Instruction*pc=getjumpcontrol(fs,e->u.s.info);
SETARG_A(*pc,!(GETARG_A(*pc)));
}
static int jumponcond(FuncState*fs,expdesc*e,int cond){
if(e->k==VRELOCABLE){
Instruction ie=getcode(fs,e);
if(GET_OPCODE(ie)==OP_NOT){
fs->pc--;
return condjump(fs,OP_TEST,GETARG_B(ie),0,!cond);
}
}
discharge2anyreg(fs,e);
freeexp(fs,e);
return condjump(fs,OP_TESTSET,((1<<8)-1),e->u.s.info,cond);
}
static void luaK_goiftrue(FuncState*fs,expdesc*e){
int pc;
luaK_dischargevars(fs,e);
switch(e->k){
case VK:case VKNUM:case VTRUE:{
pc=(-1);
break;
}
case VJMP:{
invertjump(fs,e);
pc=e->u.s.info;
break;
}
default:{
pc=jumponcond(fs,e,0);
break;
}
}
luaK_concat(fs,&e->f,pc);
luaK_patchtohere(fs,e->t);
e->t=(-1);
}
static void luaK_goiffalse(FuncState*fs,expdesc*e){
int pc;
luaK_dischargevars(fs,e);
switch(e->k){
case VNIL:case VFALSE:{
pc=(-1);
break;
}
case VJMP:{
pc=e->u.s.info;
break;
}
default:{
pc=jumponcond(fs,e,1);
break;
}
}
luaK_concat(fs,&e->t,pc);
luaK_patchtohere(fs,e->f);
e->f=(-1);
}
static void codenot(FuncState*fs,expdesc*e){
luaK_dischargevars(fs,e);
switch(e->k){
case VNIL:case VFALSE:{
e->k=VTRUE;
break;
}
case VK:case VKNUM:case VTRUE:{
e->k=VFALSE;
break;
}
case VJMP:{
invertjump(fs,e);
break;
}
case VRELOCABLE:
case VNONRELOC:{
discharge2anyreg(fs,e);
freeexp(fs,e);
e->u.s.info=luaK_codeABC(fs,OP_NOT,0,e->u.s.info,0);
e->k=VRELOCABLE;
break;
}
default:{
break;
}
}
{int temp=e->f;e->f=e->t;e->t=temp;}
removevalues(fs,e->f);
removevalues(fs,e->t);
}
static void luaK_indexed(FuncState*fs,expdesc*t,expdesc*k){
t->u.s.aux=luaK_exp2RK(fs,k);
t->k=VINDEXED;
}
static int constfolding(OpCode op,expdesc*e1,expdesc*e2){
lua_Number v1,v2,r;
if(!isnumeral(e1)||!isnumeral(e2))return 0;
v1=e1->u.nval;
v2=e2->u.nval;
switch(op){
case OP_ADD:r=luai_numadd(v1,v2);break;
case OP_SUB:r=luai_numsub(v1,v2);break;
case OP_MUL:r=luai_nummul(v1,v2);break;
case OP_DIV:
if(v2==0)return 0;
r=luai_numdiv(v1,v2);break;
case OP_MOD:
if(v2==0)return 0;
r=luai_nummod(v1,v2);break;
case OP_POW:r=luai_numpow(v1,v2);break;
case OP_UNM:r=luai_numunm(v1);break;
case OP_LEN:return 0;
default:r=0;break;
}
if(luai_numisnan(r))return 0;
e1->u.nval=r;
return 1;
}
static void codearith(FuncState*fs,OpCode op,expdesc*e1,expdesc*e2){
if(constfolding(op,e1,e2))
return;
else{
int o2=(op!=OP_UNM&&op!=OP_LEN)?luaK_exp2RK(fs,e2):0;
int o1=luaK_exp2RK(fs,e1);
if(o1>o2){
freeexp(fs,e1);
freeexp(fs,e2);
}
else{
freeexp(fs,e2);
freeexp(fs,e1);
}
e1->u.s.info=luaK_codeABC(fs,op,0,o1,o2);
e1->k=VRELOCABLE;
}
}
static void codecomp(FuncState*fs,OpCode op,int cond,expdesc*e1,
expdesc*e2){
int o1=luaK_exp2RK(fs,e1);
int o2=luaK_exp2RK(fs,e2);
freeexp(fs,e2);
freeexp(fs,e1);
if(cond==0&&op!=OP_EQ){
int temp;
temp=o1;o1=o2;o2=temp;
cond=1;
}
e1->u.s.info=condjump(fs,op,cond,o1,o2);
e1->k=VJMP;
}
static void luaK_prefix(FuncState*fs,UnOpr op,expdesc*e){
expdesc e2;
e2.t=e2.f=(-1);e2.k=VKNUM;e2.u.nval=0;
switch(op){
case OPR_MINUS:{
if(!isnumeral(e))
luaK_exp2anyreg(fs,e);
codearith(fs,OP_UNM,e,&e2);
break;
}
case OPR_NOT:codenot(fs,e);break;
case OPR_LEN:{
luaK_exp2anyreg(fs,e);
codearith(fs,OP_LEN,e,&e2);
break;
}
default:;
}
}
static void luaK_infix(FuncState*fs,BinOpr op,expdesc*v){
switch(op){
case OPR_AND:{
luaK_goiftrue(fs,v);
break;
}
case OPR_OR:{
luaK_goiffalse(fs,v);
break;
}
case OPR_CONCAT:{
luaK_exp2nextreg(fs,v);
break;
}
case OPR_ADD:case OPR_SUB:case OPR_MUL:case OPR_DIV:
case OPR_MOD:case OPR_POW:{
if(!isnumeral(v))luaK_exp2RK(fs,v);
break;
}
default:{
luaK_exp2RK(fs,v);
break;
}
}
}
static void luaK_posfix(FuncState*fs,BinOpr op,expdesc*e1,expdesc*e2){
switch(op){
case OPR_AND:{
luaK_dischargevars(fs,e2);
luaK_concat(fs,&e2->f,e1->f);
*e1=*e2;
break;
}
case OPR_OR:{
luaK_dischargevars(fs,e2);
luaK_concat(fs,&e2->t,e1->t);
*e1=*e2;
break;
}
case OPR_CONCAT:{
luaK_exp2val(fs,e2);
if(e2->k==VRELOCABLE&&GET_OPCODE(getcode(fs,e2))==OP_CONCAT){
freeexp(fs,e1);
SETARG_B(getcode(fs,e2),e1->u.s.info);
e1->k=VRELOCABLE;e1->u.s.info=e2->u.s.info;
}
else{
luaK_exp2nextreg(fs,e2);
codearith(fs,OP_CONCAT,e1,e2);
}
break;
}
case OPR_ADD:codearith(fs,OP_ADD,e1,e2);break;
case OPR_SUB:codearith(fs,OP_SUB,e1,e2);break;
case OPR_MUL:codearith(fs,OP_MUL,e1,e2);break;
case OPR_DIV:codearith(fs,OP_DIV,e1,e2);break;
case OPR_MOD:codearith(fs,OP_MOD,e1,e2);break;
case OPR_POW:codearith(fs,OP_POW,e1,e2);break;
case OPR_EQ:codecomp(fs,OP_EQ,1,e1,e2);break;
case OPR_NE:codecomp(fs,OP_EQ,0,e1,e2);break;
case OPR_LT:codecomp(fs,OP_LT,1,e1,e2);break;
case OPR_LE:codecomp(fs,OP_LE,1,e1,e2);break;
case OPR_GT:codecomp(fs,OP_LT,0,e1,e2);break;
case OPR_GE:codecomp(fs,OP_LE,0,e1,e2);break;
default:;
}
}
static void luaK_fixline(FuncState*fs,int line){
fs->f->lineinfo[fs->pc-1]=line;
}
static int luaK_code(FuncState*fs,Instruction i,int line){
Proto*f=fs->f;
dischargejpc(fs);
luaM_growvector(fs->L,f->code,fs->pc,f->sizecode,Instruction,
(INT_MAX-2),"code size overflow");
f->code[fs->pc]=i;
luaM_growvector(fs->L,f->lineinfo,fs->pc,f->sizelineinfo,int,
(INT_MAX-2),"code size overflow");
f->lineinfo[fs->pc]=line;
return fs->pc++;
}
static int luaK_codeABC(FuncState*fs,OpCode o,int a,int b,int c){
return luaK_code(fs,CREATE_ABC(o,a,b,c),fs->ls->lastline);
}
static int luaK_codeABx(FuncState*fs,OpCode o,int a,unsigned int bc){
return luaK_code(fs,CREATE_ABx(o,a,bc),fs->ls->lastline);
}
static void luaK_setlist(FuncState*fs,int base,int nelems,int tostore){
int c=(nelems-1)/50+1;
int b=(tostore==(-1))?0:tostore;
if(c<=((1<<9)-1))
luaK_codeABC(fs,OP_SETLIST,base,b,c);
else{
luaK_codeABC(fs,OP_SETLIST,base,b,0);
luaK_code(fs,cast(Instruction,c),fs->ls->lastline);
}
fs->freereg=base+1;
}
#define hasmultret(k)((k)==VCALL||(k)==VVARARG)
#define getlocvar(fs,i)((fs)->f->locvars[(fs)->actvar[i]])
#define luaY_checklimit(fs,v,l,m)if((v)>(l))errorlimit(fs,l,m)
typedef struct BlockCnt{
struct BlockCnt*previous;
int breaklist;
lu_byte nactvar;
lu_byte upval;
lu_byte isbreakable;
}BlockCnt;
static void chunk(LexState*ls);
static void expr(LexState*ls,expdesc*v);
static void anchor_token(LexState*ls){
if(ls->t.token==TK_NAME||ls->t.token==TK_STRING){
TString*ts=ls->t.seminfo.ts;
luaX_newstring(ls,getstr(ts),ts->tsv.len);
}
}
static void error_expected(LexState*ls,int token){
luaX_syntaxerror(ls,
luaO_pushfstring(ls->L,LUA_QL("%s")" expected",luaX_token2str(ls,token)));
}
static void errorlimit(FuncState*fs,int limit,const char*what){
const char*msg=(fs->f->linedefined==0)?
luaO_pushfstring(fs->L,"main function has more than %d %s",limit,what):
luaO_pushfstring(fs->L,"function at line %d has more than %d %s",
fs->f->linedefined,limit,what);
luaX_lexerror(fs->ls,msg,0);
}
static int testnext(LexState*ls,int c){
if(ls->t.token==c){
luaX_next(ls);
return 1;
}
else return 0;
}
static void check(LexState*ls,int c){
if(ls->t.token!=c)
error_expected(ls,c);
}
static void checknext(LexState*ls,int c){
check(ls,c);
luaX_next(ls);
}
#define check_condition(ls,c,msg){if(!(c))luaX_syntaxerror(ls,msg);}
static void check_match(LexState*ls,int what,int who,int where){
if(!testnext(ls,what)){
if(where==ls->linenumber)
error_expected(ls,what);
else{
luaX_syntaxerror(ls,luaO_pushfstring(ls->L,
LUA_QL("%s")" expected (to close "LUA_QL("%s")" at line %d)",
luaX_token2str(ls,what),luaX_token2str(ls,who),where));
}
}
}
static TString*str_checkname(LexState*ls){
TString*ts;
check(ls,TK_NAME);
ts=ls->t.seminfo.ts;
luaX_next(ls);
return ts;
}
static void init_exp(expdesc*e,expkind k,int i){
e->f=e->t=(-1);
e->k=k;
e->u.s.info=i;
}
static void codestring(LexState*ls,expdesc*e,TString*s){
init_exp(e,VK,luaK_stringK(ls->fs,s));
}
static void checkname(LexState*ls,expdesc*e){
codestring(ls,e,str_checkname(ls));
}
static int registerlocalvar(LexState*ls,TString*varname){
FuncState*fs=ls->fs;
Proto*f=fs->f;
int oldsize=f->sizelocvars;
luaM_growvector(ls->L,f->locvars,fs->nlocvars,f->sizelocvars,
LocVar,SHRT_MAX,"too many local variables");
while(oldsize<f->sizelocvars)f->locvars[oldsize++].varname=NULL;
f->locvars[fs->nlocvars].varname=varname;
luaC_objbarrier(ls->L,f,varname);
return fs->nlocvars++;
}
#define new_localvarliteral(ls,v,n)new_localvar(ls,luaX_newstring(ls,""v,(sizeof(v)/sizeof(char))-1),n)
static void new_localvar(LexState*ls,TString*name,int n){
FuncState*fs=ls->fs;
luaY_checklimit(fs,fs->nactvar+n+1,200,"local variables");
fs->actvar[fs->nactvar+n]=cast(unsigned short,registerlocalvar(ls,name));
}
static void adjustlocalvars(LexState*ls,int nvars){
FuncState*fs=ls->fs;
fs->nactvar=cast_byte(fs->nactvar+nvars);
for(;nvars;nvars--){
getlocvar(fs,fs->nactvar-nvars).startpc=fs->pc;
}
}
static void removevars(LexState*ls,int tolevel){
FuncState*fs=ls->fs;
while(fs->nactvar>tolevel)
getlocvar(fs,--fs->nactvar).endpc=fs->pc;
}
static int indexupvalue(FuncState*fs,TString*name,expdesc*v){
int i;
Proto*f=fs->f;
int oldsize=f->sizeupvalues;
for(i=0;i<f->nups;i++){
if(fs->upvalues[i].k==v->k&&fs->upvalues[i].info==v->u.s.info){
return i;
}
}
luaY_checklimit(fs,f->nups+1,60,"upvalues");
luaM_growvector(fs->L,f->upvalues,f->nups,f->sizeupvalues,
TString*,(INT_MAX-2),"");
while(oldsize<f->sizeupvalues)f->upvalues[oldsize++]=NULL;
f->upvalues[f->nups]=name;
luaC_objbarrier(fs->L,f,name);
fs->upvalues[f->nups].k=cast_byte(v->k);
fs->upvalues[f->nups].info=cast_byte(v->u.s.info);
return f->nups++;
}
static int searchvar(FuncState*fs,TString*n){
int i;
for(i=fs->nactvar-1;i>=0;i--){
if(n==getlocvar(fs,i).varname)
return i;
}
return-1;
}
static void markupval(FuncState*fs,int level){
BlockCnt*bl=fs->bl;
while(bl&&bl->nactvar>level)bl=bl->previous;
if(bl)bl->upval=1;
}
static int singlevaraux(FuncState*fs,TString*n,expdesc*var,int base){
if(fs==NULL){
init_exp(var,VGLOBAL,((1<<8)-1));
return VGLOBAL;
}
else{
int v=searchvar(fs,n);
if(v>=0){
init_exp(var,VLOCAL,v);
if(!base)
markupval(fs,v);
return VLOCAL;
}
else{
if(singlevaraux(fs->prev,n,var,0)==VGLOBAL)
return VGLOBAL;
var->u.s.info=indexupvalue(fs,n,var);
var->k=VUPVAL;
return VUPVAL;
}
}
}
static void singlevar(LexState*ls,expdesc*var){
TString*varname=str_checkname(ls);
FuncState*fs=ls->fs;
if(singlevaraux(fs,varname,var,1)==VGLOBAL)
var->u.s.info=luaK_stringK(fs,varname);
}
static void adjust_assign(LexState*ls,int nvars,int nexps,expdesc*e){
FuncState*fs=ls->fs;
int extra=nvars-nexps;
if(hasmultret(e->k)){
extra++;
if(extra<0)extra=0;
luaK_setreturns(fs,e,extra);
if(extra>1)luaK_reserveregs(fs,extra-1);
}
else{
if(e->k!=VVOID)luaK_exp2nextreg(fs,e);
if(extra>0){
int reg=fs->freereg;
luaK_reserveregs(fs,extra);
luaK_nil(fs,reg,extra);
}
}
}
static void enterlevel(LexState*ls){
if(++ls->L->nCcalls>200)
luaX_lexerror(ls,"chunk has too many syntax levels",0);
}
#define leavelevel(ls)((ls)->L->nCcalls--)
static void enterblock(FuncState*fs,BlockCnt*bl,lu_byte isbreakable){
bl->breaklist=(-1);
bl->isbreakable=isbreakable;
bl->nactvar=fs->nactvar;
bl->upval=0;
bl->previous=fs->bl;
fs->bl=bl;
}
static void leaveblock(FuncState*fs){
BlockCnt*bl=fs->bl;
fs->bl=bl->previous;
removevars(fs->ls,bl->nactvar);
if(bl->upval)
luaK_codeABC(fs,OP_CLOSE,bl->nactvar,0,0);
fs->freereg=fs->nactvar;
luaK_patchtohere(fs,bl->breaklist);
}
static void pushclosure(LexState*ls,FuncState*func,expdesc*v){
FuncState*fs=ls->fs;
Proto*f=fs->f;
int oldsize=f->sizep;
int i;
luaM_growvector(ls->L,f->p,fs->np,f->sizep,Proto*,
((1<<(9+9))-1),"constant table overflow");
while(oldsize<f->sizep)f->p[oldsize++]=NULL;
f->p[fs->np++]=func->f;
luaC_objbarrier(ls->L,f,func->f);
init_exp(v,VRELOCABLE,luaK_codeABx(fs,OP_CLOSURE,0,fs->np-1));
for(i=0;i<func->f->nups;i++){
OpCode o=(func->upvalues[i].k==VLOCAL)?OP_MOVE:OP_GETUPVAL;
luaK_codeABC(fs,o,0,func->upvalues[i].info,0);
}
}
static void open_func(LexState*ls,FuncState*fs){
lua_State*L=ls->L;
Proto*f=luaF_newproto(L);
fs->f=f;
fs->prev=ls->fs;
fs->ls=ls;
fs->L=L;
ls->fs=fs;
fs->pc=0;
fs->lasttarget=-1;
fs->jpc=(-1);
fs->freereg=0;
fs->nk=0;
fs->np=0;
fs->nlocvars=0;
fs->nactvar=0;
fs->bl=NULL;
f->source=ls->source;
f->maxstacksize=2;
fs->h=luaH_new(L,0,0);
sethvalue(L,L->top,fs->h);
incr_top(L);
setptvalue(L,L->top,f);
incr_top(L);
}
static void close_func(LexState*ls){
lua_State*L=ls->L;
FuncState*fs=ls->fs;
Proto*f=fs->f;
removevars(ls,0);
luaK_ret(fs,0,0);
luaM_reallocvector(L,f->code,f->sizecode,fs->pc,Instruction);
f->sizecode=fs->pc;
luaM_reallocvector(L,f->lineinfo,f->sizelineinfo,fs->pc,int);
f->sizelineinfo=fs->pc;
luaM_reallocvector(L,f->k,f->sizek,fs->nk,TValue);
f->sizek=fs->nk;
luaM_reallocvector(L,f->p,f->sizep,fs->np,Proto*);
f->sizep=fs->np;
luaM_reallocvector(L,f->locvars,f->sizelocvars,fs->nlocvars,LocVar);
f->sizelocvars=fs->nlocvars;
luaM_reallocvector(L,f->upvalues,f->sizeupvalues,f->nups,TString*);
f->sizeupvalues=f->nups;
ls->fs=fs->prev;
if(fs)anchor_token(ls);
L->top-=2;
}
static Proto*luaY_parser(lua_State*L,ZIO*z,Mbuffer*buff,const char*name){
struct LexState lexstate;
struct FuncState funcstate;
lexstate.buff=buff;
luaX_setinput(L,&lexstate,z,luaS_new(L,name));
open_func(&lexstate,&funcstate);
funcstate.f->is_vararg=2;
luaX_next(&lexstate);
chunk(&lexstate);
check(&lexstate,TK_EOS);
close_func(&lexstate);
return funcstate.f;
}
static void field(LexState*ls,expdesc*v){
FuncState*fs=ls->fs;
expdesc key;
luaK_exp2anyreg(fs,v);
luaX_next(ls);
checkname(ls,&key);
luaK_indexed(fs,v,&key);
}
static void yindex(LexState*ls,expdesc*v){
luaX_next(ls);
expr(ls,v);
luaK_exp2val(ls->fs,v);
checknext(ls,']');
}
struct ConsControl{
expdesc v;
expdesc*t;
int nh;
int na;
int tostore;
};
static void recfield(LexState*ls,struct ConsControl*cc){
FuncState*fs=ls->fs;
int reg=ls->fs->freereg;
expdesc key,val;
int rkkey;
if(ls->t.token==TK_NAME){
luaY_checklimit(fs,cc->nh,(INT_MAX-2),"items in a constructor");
checkname(ls,&key);
}
else
yindex(ls,&key);
cc->nh++;
checknext(ls,'=');
rkkey=luaK_exp2RK(fs,&key);
expr(ls,&val);
luaK_codeABC(fs,OP_SETTABLE,cc->t->u.s.info,rkkey,luaK_exp2RK(fs,&val));
fs->freereg=reg;
}
static void closelistfield(FuncState*fs,struct ConsControl*cc){
if(cc->v.k==VVOID)return;
luaK_exp2nextreg(fs,&cc->v);
cc->v.k=VVOID;
if(cc->tostore==50){
luaK_setlist(fs,cc->t->u.s.info,cc->na,cc->tostore);
cc->tostore=0;
}
}
static void lastlistfield(FuncState*fs,struct ConsControl*cc){
if(cc->tostore==0)return;
if(hasmultret(cc->v.k)){
luaK_setmultret(fs,&cc->v);
luaK_setlist(fs,cc->t->u.s.info,cc->na,(-1));
cc->na--;
}
else{
if(cc->v.k!=VVOID)
luaK_exp2nextreg(fs,&cc->v);
luaK_setlist(fs,cc->t->u.s.info,cc->na,cc->tostore);
}
}
static void listfield(LexState*ls,struct ConsControl*cc){
expr(ls,&cc->v);
luaY_checklimit(ls->fs,cc->na,(INT_MAX-2),"items in a constructor");
cc->na++;
cc->tostore++;
}
static void constructor(LexState*ls,expdesc*t){
FuncState*fs=ls->fs;
int line=ls->linenumber;
int pc=luaK_codeABC(fs,OP_NEWTABLE,0,0,0);
struct ConsControl cc;
cc.na=cc.nh=cc.tostore=0;
cc.t=t;
init_exp(t,VRELOCABLE,pc);
init_exp(&cc.v,VVOID,0);
luaK_exp2nextreg(ls->fs,t);
checknext(ls,'{');
do{
if(ls->t.token=='}')break;
closelistfield(fs,&cc);
switch(ls->t.token){
case TK_NAME:{
luaX_lookahead(ls);
if(ls->lookahead.token!='=')
listfield(ls,&cc);
else
recfield(ls,&cc);
break;
}
case'[':{
recfield(ls,&cc);
break;
}
default:{
listfield(ls,&cc);
break;
}
}
}while(testnext(ls,',')||testnext(ls,';'));
check_match(ls,'}','{',line);
lastlistfield(fs,&cc);
SETARG_B(fs->f->code[pc],luaO_int2fb(cc.na));
SETARG_C(fs->f->code[pc],luaO_int2fb(cc.nh));
}
static void parlist(LexState*ls){
FuncState*fs=ls->fs;
Proto*f=fs->f;
int nparams=0;
f->is_vararg=0;
if(ls->t.token!=')'){
do{
switch(ls->t.token){
case TK_NAME:{
new_localvar(ls,str_checkname(ls),nparams++);
break;
}
case TK_DOTS:{
luaX_next(ls);
f->is_vararg|=2;
break;
}
default:luaX_syntaxerror(ls,"<name> or "LUA_QL("...")" expected");
}
}while(!f->is_vararg&&testnext(ls,','));
}
adjustlocalvars(ls,nparams);
f->numparams=cast_byte(fs->nactvar-(f->is_vararg&1));
luaK_reserveregs(fs,fs->nactvar);
}
static void body(LexState*ls,expdesc*e,int needself,int line){
FuncState new_fs;
open_func(ls,&new_fs);
new_fs.f->linedefined=line;
checknext(ls,'(');
if(needself){
new_localvarliteral(ls,"self",0);
adjustlocalvars(ls,1);
}
parlist(ls);
checknext(ls,')');
chunk(ls);
new_fs.f->lastlinedefined=ls->linenumber;
check_match(ls,TK_END,TK_FUNCTION,line);
close_func(ls);
pushclosure(ls,&new_fs,e);
}
static int explist1(LexState*ls,expdesc*v){
int n=1;
expr(ls,v);
while(testnext(ls,',')){
luaK_exp2nextreg(ls->fs,v);
expr(ls,v);
n++;
}
return n;
}
static void funcargs(LexState*ls,expdesc*f){
FuncState*fs=ls->fs;
expdesc args;
int base,nparams;
int line=ls->linenumber;
switch(ls->t.token){
case'(':{
if(line!=ls->lastline)
luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)");
luaX_next(ls);
if(ls->t.token==')')
args.k=VVOID;
else{
explist1(ls,&args);
luaK_setmultret(fs,&args);
}
check_match(ls,')','(',line);
break;
}
case'{':{
constructor(ls,&args);
break;
}
case TK_STRING:{
codestring(ls,&args,ls->t.seminfo.ts);
luaX_next(ls);
break;
}
default:{
luaX_syntaxerror(ls,"function arguments expected");
return;
}
}
base=f->u.s.info;
if(hasmultret(args.k))
nparams=(-1);
else{
if(args.k!=VVOID)
luaK_exp2nextreg(fs,&args);
nparams=fs->freereg-(base+1);
}
init_exp(f,VCALL,luaK_codeABC(fs,OP_CALL,base,nparams+1,2));
luaK_fixline(fs,line);
fs->freereg=base+1;
}
static void prefixexp(LexState*ls,expdesc*v){
switch(ls->t.token){
case'(':{
int line=ls->linenumber;
luaX_next(ls);
expr(ls,v);
check_match(ls,')','(',line);
luaK_dischargevars(ls->fs,v);
return;
}
case TK_NAME:{
singlevar(ls,v);
return;
}
default:{
luaX_syntaxerror(ls,"unexpected symbol");
return;
}
}
}
static void primaryexp(LexState*ls,expdesc*v){
FuncState*fs=ls->fs;
prefixexp(ls,v);
for(;;){
switch(ls->t.token){
case'.':{
field(ls,v);
break;
}
case'[':{
expdesc key;
luaK_exp2anyreg(fs,v);
yindex(ls,&key);
luaK_indexed(fs,v,&key);
break;
}
case':':{
expdesc key;
luaX_next(ls);
checkname(ls,&key);
luaK_self(fs,v,&key);
funcargs(ls,v);
break;
}
case'(':case TK_STRING:case'{':{
luaK_exp2nextreg(fs,v);
funcargs(ls,v);
break;
}
default:return;
}
}
}
static void simpleexp(LexState*ls,expdesc*v){
switch(ls->t.token){
case TK_NUMBER:{
init_exp(v,VKNUM,0);
v->u.nval=ls->t.seminfo.r;
break;
}
case TK_STRING:{
codestring(ls,v,ls->t.seminfo.ts);
break;
}
case TK_NIL:{
init_exp(v,VNIL,0);
break;
}
case TK_TRUE:{
init_exp(v,VTRUE,0);
break;
}
case TK_FALSE:{
init_exp(v,VFALSE,0);
break;
}
case TK_DOTS:{
FuncState*fs=ls->fs;
check_condition(ls,fs->f->is_vararg,
"cannot use "LUA_QL("...")" outside a vararg function");
fs->f->is_vararg&=~4;
init_exp(v,VVARARG,luaK_codeABC(fs,OP_VARARG,0,1,0));
break;
}
case'{':{
constructor(ls,v);
return;
}
case TK_FUNCTION:{
luaX_next(ls);
body(ls,v,0,ls->linenumber);
return;
}
default:{
primaryexp(ls,v);
return;
}
}
luaX_next(ls);
}
static UnOpr getunopr(int op){
switch(op){
case TK_NOT:return OPR_NOT;
case'-':return OPR_MINUS;
case'#':return OPR_LEN;
default:return OPR_NOUNOPR;
}
}
static BinOpr getbinopr(int op){
switch(op){
case'+':return OPR_ADD;
case'-':return OPR_SUB;
case'*':return OPR_MUL;
case'/':return OPR_DIV;
case'%':return OPR_MOD;
case'^':return OPR_POW;
case TK_CONCAT:return OPR_CONCAT;
case TK_NE:return OPR_NE;
case TK_EQ:return OPR_EQ;
case'<':return OPR_LT;
case TK_LE:return OPR_LE;
case'>':return OPR_GT;
case TK_GE:return OPR_GE;
case TK_AND:return OPR_AND;
case TK_OR:return OPR_OR;
default:return OPR_NOBINOPR;
}
}
static const struct{
lu_byte left;
lu_byte right;
}priority[]={
{6,6},{6,6},{7,7},{7,7},{7,7},
{10,9},{5,4},
{3,3},{3,3},
{3,3},{3,3},{3,3},{3,3},
{2,2},{1,1}
};
static BinOpr subexpr(LexState*ls,expdesc*v,unsigned int limit){
BinOpr op;
UnOpr uop;
enterlevel(ls);
uop=getunopr(ls->t.token);
if(uop!=OPR_NOUNOPR){
luaX_next(ls);
subexpr(ls,v,8);
luaK_prefix(ls->fs,uop,v);
}
else simpleexp(ls,v);
op=getbinopr(ls->t.token);
while(op!=OPR_NOBINOPR&&priority[op].left>limit){
expdesc v2;
BinOpr nextop;
luaX_next(ls);
luaK_infix(ls->fs,op,v);
nextop=subexpr(ls,&v2,priority[op].right);
luaK_posfix(ls->fs,op,v,&v2);
op=nextop;
}
leavelevel(ls);
return op;
}
static void expr(LexState*ls,expdesc*v){
subexpr(ls,v,0);
}
static int block_follow(int token){
switch(token){
case TK_ELSE:case TK_ELSEIF:case TK_END:
case TK_UNTIL:case TK_EOS:
return 1;
default:return 0;
}
}
static void block(LexState*ls){
FuncState*fs=ls->fs;
BlockCnt bl;
enterblock(fs,&bl,0);
chunk(ls);
leaveblock(fs);
}
struct LHS_assign{
struct LHS_assign*prev;
expdesc v;
};
static void check_conflict(LexState*ls,struct LHS_assign*lh,expdesc*v){
FuncState*fs=ls->fs;
int extra=fs->freereg;
int conflict=0;
for(;lh;lh=lh->prev){
if(lh->v.k==VINDEXED){
if(lh->v.u.s.info==v->u.s.info){
conflict=1;
lh->v.u.s.info=extra;
}
if(lh->v.u.s.aux==v->u.s.info){
conflict=1;
lh->v.u.s.aux=extra;
}
}
}
if(conflict){
luaK_codeABC(fs,OP_MOVE,fs->freereg,v->u.s.info,0);
luaK_reserveregs(fs,1);
}
}
static void assignment(LexState*ls,struct LHS_assign*lh,int nvars){
expdesc e;
check_condition(ls,VLOCAL<=lh->v.k&&lh->v.k<=VINDEXED,
"syntax error");
if(testnext(ls,',')){
struct LHS_assign nv;
nv.prev=lh;
primaryexp(ls,&nv.v);
if(nv.v.k==VLOCAL)
check_conflict(ls,lh,&nv.v);
luaY_checklimit(ls->fs,nvars,200-ls->L->nCcalls,
"variables in assignment");
assignment(ls,&nv,nvars+1);
}
else{
int nexps;
checknext(ls,'=');
nexps=explist1(ls,&e);
if(nexps!=nvars){
adjust_assign(ls,nvars,nexps,&e);
if(nexps>nvars)
ls->fs->freereg-=nexps-nvars;
}
else{
luaK_setoneret(ls->fs,&e);
luaK_storevar(ls->fs,&lh->v,&e);
return;
}
}
init_exp(&e,VNONRELOC,ls->fs->freereg-1);
luaK_storevar(ls->fs,&lh->v,&e);
}
static int cond(LexState*ls){
expdesc v;
expr(ls,&v);
if(v.k==VNIL)v.k=VFALSE;
luaK_goiftrue(ls->fs,&v);
return v.f;
}
static void breakstat(LexState*ls){
FuncState*fs=ls->fs;
BlockCnt*bl=fs->bl;
int upval=0;
while(bl&&!bl->isbreakable){
upval|=bl->upval;
bl=bl->previous;
}
if(!bl)
luaX_syntaxerror(ls,"no loop to break");
if(upval)
luaK_codeABC(fs,OP_CLOSE,bl->nactvar,0,0);
luaK_concat(fs,&bl->breaklist,luaK_jump(fs));
}
static void whilestat(LexState*ls,int line){
FuncState*fs=ls->fs;
int whileinit;
int condexit;
BlockCnt bl;
luaX_next(ls);
whileinit=luaK_getlabel(fs);
condexit=cond(ls);
enterblock(fs,&bl,1);
checknext(ls,TK_DO);
block(ls);
luaK_patchlist(fs,luaK_jump(fs),whileinit);
check_match(ls,TK_END,TK_WHILE,line);
leaveblock(fs);
luaK_patchtohere(fs,condexit);
}
static void repeatstat(LexState*ls,int line){
int condexit;
FuncState*fs=ls->fs;
int repeat_init=luaK_getlabel(fs);
BlockCnt bl1,bl2;
enterblock(fs,&bl1,1);
enterblock(fs,&bl2,0);
luaX_next(ls);
chunk(ls);
check_match(ls,TK_UNTIL,TK_REPEAT,line);
condexit=cond(ls);
if(!bl2.upval){
leaveblock(fs);
luaK_patchlist(ls->fs,condexit,repeat_init);
}
else{
breakstat(ls);
luaK_patchtohere(ls->fs,condexit);
leaveblock(fs);
luaK_patchlist(ls->fs,luaK_jump(fs),repeat_init);
}
leaveblock(fs);
}
static int exp1(LexState*ls){
expdesc e;
int k;
expr(ls,&e);
k=e.k;
luaK_exp2nextreg(ls->fs,&e);
return k;
}
static void forbody(LexState*ls,int base,int line,int nvars,int isnum){
BlockCnt bl;
FuncState*fs=ls->fs;
int prep,endfor;
adjustlocalvars(ls,3);
checknext(ls,TK_DO);
prep=isnum?luaK_codeAsBx(fs,OP_FORPREP,base,(-1)):luaK_jump(fs);
enterblock(fs,&bl,0);
adjustlocalvars(ls,nvars);
luaK_reserveregs(fs,nvars);
block(ls);
leaveblock(fs);
luaK_patchtohere(fs,prep);
endfor=(isnum)?luaK_codeAsBx(fs,OP_FORLOOP,base,(-1)):
luaK_codeABC(fs,OP_TFORLOOP,base,0,nvars);
luaK_fixline(fs,line);
luaK_patchlist(fs,(isnum?endfor:luaK_jump(fs)),prep+1);
}
static void fornum(LexState*ls,TString*varname,int line){
FuncState*fs=ls->fs;
int base=fs->freereg;
new_localvarliteral(ls,"(for index)",0);
new_localvarliteral(ls,"(for limit)",1);
new_localvarliteral(ls,"(for step)",2);
new_localvar(ls,varname,3);
checknext(ls,'=');
exp1(ls);
checknext(ls,',');
exp1(ls);
if(testnext(ls,','))
exp1(ls);
else{
luaK_codeABx(fs,OP_LOADK,fs->freereg,luaK_numberK(fs,1));
luaK_reserveregs(fs,1);
}
forbody(ls,base,line,1,1);
}
static void forlist(LexState*ls,TString*indexname){
FuncState*fs=ls->fs;
expdesc e;
int nvars=0;
int line;
int base=fs->freereg;
new_localvarliteral(ls,"(for generator)",nvars++);
new_localvarliteral(ls,"(for state)",nvars++);
new_localvarliteral(ls,"(for control)",nvars++);
new_localvar(ls,indexname,nvars++);
while(testnext(ls,','))
new_localvar(ls,str_checkname(ls),nvars++);
checknext(ls,TK_IN);
line=ls->linenumber;
adjust_assign(ls,3,explist1(ls,&e),&e);
luaK_checkstack(fs,3);
forbody(ls,base,line,nvars-3,0);
}
static void forstat(LexState*ls,int line){
FuncState*fs=ls->fs;
TString*varname;
BlockCnt bl;
enterblock(fs,&bl,1);
luaX_next(ls);
varname=str_checkname(ls);
switch(ls->t.token){
case'=':fornum(ls,varname,line);break;
case',':case TK_IN:forlist(ls,varname);break;
default:luaX_syntaxerror(ls,LUA_QL("=")" or "LUA_QL("in")" expected");
}
check_match(ls,TK_END,TK_FOR,line);
leaveblock(fs);
}
static int test_then_block(LexState*ls){
int condexit;
luaX_next(ls);
condexit=cond(ls);
checknext(ls,TK_THEN);
block(ls);
return condexit;
}
static void ifstat(LexState*ls,int line){
FuncState*fs=ls->fs;
int flist;
int escapelist=(-1);
flist=test_then_block(ls);
while(ls->t.token==TK_ELSEIF){
luaK_concat(fs,&escapelist,luaK_jump(fs));
luaK_patchtohere(fs,flist);
flist=test_then_block(ls);
}
if(ls->t.token==TK_ELSE){
luaK_concat(fs,&escapelist,luaK_jump(fs));
luaK_patchtohere(fs,flist);
luaX_next(ls);
block(ls);
}
else
luaK_concat(fs,&escapelist,flist);
luaK_patchtohere(fs,escapelist);
check_match(ls,TK_END,TK_IF,line);
}
static void localfunc(LexState*ls){
expdesc v,b;
FuncState*fs=ls->fs;
new_localvar(ls,str_checkname(ls),0);
init_exp(&v,VLOCAL,fs->freereg);
luaK_reserveregs(fs,1);
adjustlocalvars(ls,1);
body(ls,&b,0,ls->linenumber);
luaK_storevar(fs,&v,&b);
getlocvar(fs,fs->nactvar-1).startpc=fs->pc;
}
static void localstat(LexState*ls){
int nvars=0;
int nexps;
expdesc e;
do{
new_localvar(ls,str_checkname(ls),nvars++);
}while(testnext(ls,','));
if(testnext(ls,'='))
nexps=explist1(ls,&e);
else{
e.k=VVOID;
nexps=0;
}
adjust_assign(ls,nvars,nexps,&e);
adjustlocalvars(ls,nvars);
}
static int funcname(LexState*ls,expdesc*v){
int needself=0;
singlevar(ls,v);
while(ls->t.token=='.')
field(ls,v);
if(ls->t.token==':'){
needself=1;
field(ls,v);
}
return needself;
}
static void funcstat(LexState*ls,int line){
int needself;
expdesc v,b;
luaX_next(ls);
needself=funcname(ls,&v);
body(ls,&b,needself,line);
luaK_storevar(ls->fs,&v,&b);
luaK_fixline(ls->fs,line);
}
static void exprstat(LexState*ls){
FuncState*fs=ls->fs;
struct LHS_assign v;
primaryexp(ls,&v.v);
if(v.v.k==VCALL)
SETARG_C(getcode(fs,&v.v),1);
else{
v.prev=NULL;
assignment(ls,&v,1);
}
}
static void retstat(LexState*ls){
FuncState*fs=ls->fs;
expdesc e;
int first,nret;
luaX_next(ls);
if(block_follow(ls->t.token)||ls->t.token==';')
first=nret=0;
else{
nret=explist1(ls,&e);
if(hasmultret(e.k)){
luaK_setmultret(fs,&e);
if(e.k==VCALL&&nret==1){
SET_OPCODE(getcode(fs,&e),OP_TAILCALL);
}
first=fs->nactvar;
nret=(-1);
}
else{
if(nret==1)
first=luaK_exp2anyreg(fs,&e);
else{
luaK_exp2nextreg(fs,&e);
first=fs->nactvar;
}
}
}
luaK_ret(fs,first,nret);
}
static int statement(LexState*ls){
int line=ls->linenumber;
switch(ls->t.token){
case TK_IF:{
ifstat(ls,line);
return 0;
}
case TK_WHILE:{
whilestat(ls,line);
return 0;
}
case TK_DO:{
luaX_next(ls);
block(ls);
check_match(ls,TK_END,TK_DO,line);
return 0;
}
case TK_FOR:{
forstat(ls,line);
return 0;
}
case TK_REPEAT:{
repeatstat(ls,line);
return 0;
}
case TK_FUNCTION:{
funcstat(ls,line);
return 0;
}
case TK_LOCAL:{
luaX_next(ls);
if(testnext(ls,TK_FUNCTION))
localfunc(ls);
else
localstat(ls);
return 0;
}
case TK_RETURN:{
retstat(ls);
return 1;
}
case TK_BREAK:{
luaX_next(ls);
breakstat(ls);
return 1;
}
default:{
exprstat(ls);
return 0;
}
}
}
static void chunk(LexState*ls){
int islast=0;
enterlevel(ls);
while(!islast&&!block_follow(ls->t.token)){
islast=statement(ls);
testnext(ls,';');
ls->fs->freereg=ls->fs->nactvar;
}
leavelevel(ls);
}
static const TValue*luaV_tonumber(const TValue*obj,TValue*n){
lua_Number num;
if(ttisnumber(obj))return obj;
if(ttisstring(obj)&&luaO_str2d(svalue(obj),&num)){
setnvalue(n,num);
return n;
}
else
return NULL;
}
static int luaV_tostring(lua_State*L,StkId obj){
if(!ttisnumber(obj))
return 0;
else{
char s[32];
lua_Number n=nvalue(obj);
lua_number2str(s,n);
setsvalue(L,obj,luaS_new(L,s));
return 1;
}
}
static void callTMres(lua_State*L,StkId res,const TValue*f,
const TValue*p1,const TValue*p2){
ptrdiff_t result=savestack(L,res);
setobj(L,L->top,f);
setobj(L,L->top+1,p1);
setobj(L,L->top+2,p2);
luaD_checkstack(L,3);
L->top+=3;
luaD_call(L,L->top-3,1);
res=restorestack(L,result);
L->top--;
setobj(L,res,L->top);
}
static void callTM(lua_State*L,const TValue*f,const TValue*p1,
const TValue*p2,const TValue*p3){
setobj(L,L->top,f);
setobj(L,L->top+1,p1);
setobj(L,L->top+2,p2);
setobj(L,L->top+3,p3);
luaD_checkstack(L,4);
L->top+=4;
luaD_call(L,L->top-4,0);
}
static void luaV_gettable(lua_State*L,const TValue*t,TValue*key,StkId val){
int loop;
for(loop=0;loop<100;loop++){
const TValue*tm;
if(ttistable(t)){
Table*h=hvalue(t);
const TValue*res=luaH_get(h,key);
if(!ttisnil(res)||
(tm=fasttm(L,h->metatable,TM_INDEX))==NULL){
setobj(L,val,res);
return;
}
}
else if(ttisnil(tm=luaT_gettmbyobj(L,t,TM_INDEX)))
luaG_typeerror(L,t,"index");
if(ttisfunction(tm)){
callTMres(L,val,tm,t,key);
return;
}
t=tm;
}
luaG_runerror(L,"loop in gettable");
}
static void luaV_settable(lua_State*L,const TValue*t,TValue*key,StkId val){
int loop;
TValue temp;
for(loop=0;loop<100;loop++){
const TValue*tm;
if(ttistable(t)){
Table*h=hvalue(t);
TValue*oldval=luaH_set(L,h,key);
if(!ttisnil(oldval)||
(tm=fasttm(L,h->metatable,TM_NEWINDEX))==NULL){
setobj(L,oldval,val);
h->flags=0;
luaC_barriert(L,h,val);
return;
}
}
else if(ttisnil(tm=luaT_gettmbyobj(L,t,TM_NEWINDEX)))
luaG_typeerror(L,t,"index");
if(ttisfunction(tm)){
callTM(L,tm,t,key,val);
return;
}
setobj(L,&temp,tm);
t=&temp;
}
luaG_runerror(L,"loop in settable");
}
static int call_binTM(lua_State*L,const TValue*p1,const TValue*p2,
StkId res,TMS event){
const TValue*tm=luaT_gettmbyobj(L,p1,event);
if(ttisnil(tm))
tm=luaT_gettmbyobj(L,p2,event);
if(ttisnil(tm))return 0;
callTMres(L,res,tm,p1,p2);
return 1;
}
static const TValue*get_compTM(lua_State*L,Table*mt1,Table*mt2,
TMS event){
const TValue*tm1=fasttm(L,mt1,event);
const TValue*tm2;
if(tm1==NULL)return NULL;
if(mt1==mt2)return tm1;
tm2=fasttm(L,mt2,event);
if(tm2==NULL)return NULL;
if(luaO_rawequalObj(tm1,tm2))
return tm1;
return NULL;
}
static int call_orderTM(lua_State*L,const TValue*p1,const TValue*p2,
TMS event){
const TValue*tm1=luaT_gettmbyobj(L,p1,event);
const TValue*tm2;
if(ttisnil(tm1))return-1;
tm2=luaT_gettmbyobj(L,p2,event);
if(!luaO_rawequalObj(tm1,tm2))
return-1;
callTMres(L,L->top,tm1,p1,p2);
return!l_isfalse(L->top);
}
static int l_strcmp(const TString*ls,const TString*rs){
const char*l=getstr(ls);
size_t ll=ls->tsv.len;
const char*r=getstr(rs);
size_t lr=rs->tsv.len;
for(;;){
int temp=strcoll(l,r);
if(temp!=0)return temp;
else{
size_t len=strlen(l);
if(len==lr)
return(len==ll)?0:1;
else if(len==ll)
return-1;
len++;
l+=len;ll-=len;r+=len;lr-=len;
}
}
}
static int luaV_lessthan(lua_State*L,const TValue*l,const TValue*r){
int res;
if(ttype(l)!=ttype(r))
return luaG_ordererror(L,l,r);
else if(ttisnumber(l))
return luai_numlt(nvalue(l),nvalue(r));
else if(ttisstring(l))
return l_strcmp(rawtsvalue(l),rawtsvalue(r))<0;
else if((res=call_orderTM(L,l,r,TM_LT))!=-1)
return res;
return luaG_ordererror(L,l,r);
}
static int lessequal(lua_State*L,const TValue*l,const TValue*r){
int res;
if(ttype(l)!=ttype(r))
return luaG_ordererror(L,l,r);
else if(ttisnumber(l))
return luai_numle(nvalue(l),nvalue(r));
else if(ttisstring(l))
return l_strcmp(rawtsvalue(l),rawtsvalue(r))<=0;
else if((res=call_orderTM(L,l,r,TM_LE))!=-1)
return res;
else if((res=call_orderTM(L,r,l,TM_LT))!=-1)
return!res;
return luaG_ordererror(L,l,r);
}
static int luaV_equalval(lua_State*L,const TValue*t1,const TValue*t2){
const TValue*tm;
switch(ttype(t1)){
case 0:return 1;
case 3:return luai_numeq(nvalue(t1),nvalue(t2));
case 1:return bvalue(t1)==bvalue(t2);
case 2:return pvalue(t1)==pvalue(t2);
case 7:{
if(uvalue(t1)==uvalue(t2))return 1;
tm=get_compTM(L,uvalue(t1)->metatable,uvalue(t2)->metatable,
TM_EQ);
break;
}
case 5:{
if(hvalue(t1)==hvalue(t2))return 1;
tm=get_compTM(L,hvalue(t1)->metatable,hvalue(t2)->metatable,TM_EQ);
break;
}
default:return gcvalue(t1)==gcvalue(t2);
}
if(tm==NULL)return 0;
callTMres(L,L->top,tm,t1,t2);
return!l_isfalse(L->top);
}
static void luaV_concat(lua_State*L,int total,int last){
do{
StkId top=L->base+last+1;
int n=2;
if(!(ttisstring(top-2)||ttisnumber(top-2))||!tostring(L,top-1)){
if(!call_binTM(L,top-2,top-1,top-2,TM_CONCAT))
luaG_concaterror(L,top-2,top-1);
}else if(tsvalue(top-1)->len==0)
(void)tostring(L,top-2);
else{
size_t tl=tsvalue(top-1)->len;
char*buffer;
int i;
for(n=1;n<total&&tostring(L,top-n-1);n++){
size_t l=tsvalue(top-n-1)->len;
if(l>=((size_t)(~(size_t)0)-2)-tl)luaG_runerror(L,"string length overflow");
tl+=l;
}
buffer=luaZ_openspace(L,&G(L)->buff,tl);
tl=0;
for(i=n;i>0;i--){
size_t l=tsvalue(top-i)->len;
memcpy(buffer+tl,svalue(top-i),l);
tl+=l;
}
setsvalue(L,top-n,luaS_newlstr(L,buffer,tl));
}
total-=n-1;
last-=n-1;
}while(total>1);
}
static void Arith(lua_State*L,StkId ra,const TValue*rb,
const TValue*rc,TMS op){
TValue tempb,tempc;
const TValue*b,*c;
if((b=luaV_tonumber(rb,&tempb))!=NULL&&
(c=luaV_tonumber(rc,&tempc))!=NULL){
lua_Number nb=nvalue(b),nc=nvalue(c);
switch(op){
case TM_ADD:setnvalue(ra,luai_numadd(nb,nc));break;
case TM_SUB:setnvalue(ra,luai_numsub(nb,nc));break;
case TM_MUL:setnvalue(ra,luai_nummul(nb,nc));break;
case TM_DIV:setnvalue(ra,luai_numdiv(nb,nc));break;
case TM_MOD:setnvalue(ra,luai_nummod(nb,nc));break;
case TM_POW:setnvalue(ra,luai_numpow(nb,nc));break;
case TM_UNM:setnvalue(ra,luai_numunm(nb));break;
default:break;
}
}
else if(!call_binTM(L,rb,rc,ra,op))
luaG_aritherror(L,rb,rc);
}
#define runtime_check(L,c){if(!(c))break;}
#define RA(i)(base+GETARG_A(i))
#define RB(i)check_exp(getBMode(GET_OPCODE(i))==OpArgR,base+GETARG_B(i))
#define RKB(i)check_exp(getBMode(GET_OPCODE(i))==OpArgK,ISK(GETARG_B(i))?k+INDEXK(GETARG_B(i)):base+GETARG_B(i))
#define RKC(i)check_exp(getCMode(GET_OPCODE(i))==OpArgK,ISK(GETARG_C(i))?k+INDEXK(GETARG_C(i)):base+GETARG_C(i))
#define KBx(i)check_exp(getBMode(GET_OPCODE(i))==OpArgK,k+GETARG_Bx(i))
#define dojump(L,pc,i){(pc)+=(i);}
#define Protect(x){L->savedpc=pc;{x;};base=L->base;}
#define arith_op(op,tm){TValue*rb=RKB(i);TValue*rc=RKC(i);if(ttisnumber(rb)&&ttisnumber(rc)){lua_Number nb=nvalue(rb),nc=nvalue(rc);setnvalue(ra,op(nb,nc));}else Protect(Arith(L,ra,rb,rc,tm));}
static void luaV_execute(lua_State*L,int nexeccalls){
LClosure*cl;
StkId base;
TValue*k;
const Instruction*pc;
reentry:
pc=L->savedpc;
cl=&clvalue(L->ci->func)->l;
base=L->base;
k=cl->p->k;
for(;;){
const Instruction i=*pc++;
StkId ra;
ra=RA(i);
switch(GET_OPCODE(i)){
case OP_MOVE:{
setobj(L,ra,RB(i));
continue;
}
case OP_LOADK:{
setobj(L,ra,KBx(i));
continue;
}
case OP_LOADBOOL:{
setbvalue(ra,GETARG_B(i));
if(GETARG_C(i))pc++;
continue;
}
case OP_LOADNIL:{
TValue*rb=RB(i);
do{
setnilvalue(rb--);
}while(rb>=ra);
continue;
}
case OP_GETUPVAL:{
int b=GETARG_B(i);
setobj(L,ra,cl->upvals[b]->v);
continue;
}
case OP_GETGLOBAL:{
TValue g;
TValue*rb=KBx(i);
sethvalue(L,&g,cl->env);
Protect(luaV_gettable(L,&g,rb,ra));
continue;
}
case OP_GETTABLE:{
Protect(luaV_gettable(L,RB(i),RKC(i),ra));
continue;
}
case OP_SETGLOBAL:{
TValue g;
sethvalue(L,&g,cl->env);
Protect(luaV_settable(L,&g,KBx(i),ra));
continue;
}
case OP_SETUPVAL:{
UpVal*uv=cl->upvals[GETARG_B(i)];
setobj(L,uv->v,ra);
luaC_barrier(L,uv,ra);
continue;
}
case OP_SETTABLE:{
Protect(luaV_settable(L,ra,RKB(i),RKC(i)));
continue;
}
case OP_NEWTABLE:{
int b=GETARG_B(i);
int c=GETARG_C(i);
sethvalue(L,ra,luaH_new(L,luaO_fb2int(b),luaO_fb2int(c)));
Protect(luaC_checkGC(L));
continue;
}
case OP_SELF:{
StkId rb=RB(i);
setobj(L,ra+1,rb);
Protect(luaV_gettable(L,rb,RKC(i),ra));
continue;
}
case OP_ADD:{
arith_op(luai_numadd,TM_ADD);
continue;
}
case OP_SUB:{
arith_op(luai_numsub,TM_SUB);
continue;
}
case OP_MUL:{
arith_op(luai_nummul,TM_MUL);
continue;
}
case OP_DIV:{
arith_op(luai_numdiv,TM_DIV);
continue;
}
case OP_MOD:{
arith_op(luai_nummod,TM_MOD);
continue;
}
case OP_POW:{
arith_op(luai_numpow,TM_POW);
continue;
}
case OP_UNM:{
TValue*rb=RB(i);
if(ttisnumber(rb)){
lua_Number nb=nvalue(rb);
setnvalue(ra,luai_numunm(nb));
}
else{
Protect(Arith(L,ra,rb,rb,TM_UNM));
}
continue;
}
case OP_NOT:{
int res=l_isfalse(RB(i));
setbvalue(ra,res);
continue;
}
case OP_LEN:{
const TValue*rb=RB(i);
switch(ttype(rb)){
case 5:{
setnvalue(ra,cast_num(luaH_getn(hvalue(rb))));
break;
}
case 4:{
setnvalue(ra,cast_num(tsvalue(rb)->len));
break;
}
default:{
Protect(
if(!call_binTM(L,rb,(&luaO_nilobject_),ra,TM_LEN))
luaG_typeerror(L,rb,"get length of");
)
}
}
continue;
}
case OP_CONCAT:{
int b=GETARG_B(i);
int c=GETARG_C(i);
Protect(luaV_concat(L,c-b+1,c);luaC_checkGC(L));
setobj(L,RA(i),base+b);
continue;
}
case OP_JMP:{
dojump(L,pc,GETARG_sBx(i));
continue;
}
case OP_EQ:{
TValue*rb=RKB(i);
TValue*rc=RKC(i);
Protect(
if(equalobj(L,rb,rc)==GETARG_A(i))
dojump(L,pc,GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_LT:{
Protect(
if(luaV_lessthan(L,RKB(i),RKC(i))==GETARG_A(i))
dojump(L,pc,GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_LE:{
Protect(
if(lessequal(L,RKB(i),RKC(i))==GETARG_A(i))
dojump(L,pc,GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_TEST:{
if(l_isfalse(ra)!=GETARG_C(i))
dojump(L,pc,GETARG_sBx(*pc));
pc++;
continue;
}
case OP_TESTSET:{
TValue*rb=RB(i);
if(l_isfalse(rb)!=GETARG_C(i)){
setobj(L,ra,rb);
dojump(L,pc,GETARG_sBx(*pc));
}
pc++;
continue;
}
case OP_CALL:{
int b=GETARG_B(i);
int nresults=GETARG_C(i)-1;
if(b!=0)L->top=ra+b;
L->savedpc=pc;
switch(luaD_precall(L,ra,nresults)){
case 0:{
nexeccalls++;
goto reentry;
}
case 1:{
if(nresults>=0)L->top=L->ci->top;
base=L->base;
continue;
}
default:{
return;
}
}
}
case OP_TAILCALL:{
int b=GETARG_B(i);
if(b!=0)L->top=ra+b;
L->savedpc=pc;
switch(luaD_precall(L,ra,(-1))){
case 0:{
CallInfo*ci=L->ci-1;
int aux;
StkId func=ci->func;
StkId pfunc=(ci+1)->func;
if(L->openupval)luaF_close(L,ci->base);
L->base=ci->base=ci->func+((ci+1)->base-pfunc);
for(aux=0;pfunc+aux<L->top;aux++)
setobj(L,func+aux,pfunc+aux);
ci->top=L->top=func+aux;
ci->savedpc=L->savedpc;
ci->tailcalls++;
L->ci--;
goto reentry;
}
case 1:{
base=L->base;
continue;
}
default:{
return;
}
}
}
case OP_RETURN:{
int b=GETARG_B(i);
if(b!=0)L->top=ra+b-1;
if(L->openupval)luaF_close(L,base);
L->savedpc=pc;
b=luaD_poscall(L,ra);
if(--nexeccalls==0)
return;
else{
if(b)L->top=L->ci->top;
goto reentry;
}
}
case OP_FORLOOP:{
lua_Number step=nvalue(ra+2);
lua_Number idx=luai_numadd(nvalue(ra),step);
lua_Number limit=nvalue(ra+1);
if(luai_numlt(0,step)?luai_numle(idx,limit)
:luai_numle(limit,idx)){
dojump(L,pc,GETARG_sBx(i));
setnvalue(ra,idx);
setnvalue(ra+3,idx);
}
continue;
}
case OP_FORPREP:{
const TValue*init=ra;
const TValue*plimit=ra+1;
const TValue*pstep=ra+2;
L->savedpc=pc;
if(!tonumber(init,ra))
luaG_runerror(L,LUA_QL("for")" initial value must be a number");
else if(!tonumber(plimit,ra+1))
luaG_runerror(L,LUA_QL("for")" limit must be a number");
else if(!tonumber(pstep,ra+2))
luaG_runerror(L,LUA_QL("for")" step must be a number");
setnvalue(ra,luai_numsub(nvalue(ra),nvalue(pstep)));
dojump(L,pc,GETARG_sBx(i));
continue;
}
case OP_TFORLOOP:{
StkId cb=ra+3;
setobj(L,cb+2,ra+2);
setobj(L,cb+1,ra+1);
setobj(L,cb,ra);
L->top=cb+3;
Protect(luaD_call(L,cb,GETARG_C(i)));
L->top=L->ci->top;
cb=RA(i)+3;
if(!ttisnil(cb)){
setobj(L,cb-1,cb);
dojump(L,pc,GETARG_sBx(*pc));
}
pc++;
continue;
}
case OP_SETLIST:{
int n=GETARG_B(i);
int c=GETARG_C(i);
int last;
Table*h;
if(n==0){
n=cast_int(L->top-ra)-1;
L->top=L->ci->top;
}
if(c==0)c=cast_int(*pc++);
runtime_check(L,ttistable(ra));
h=hvalue(ra);
last=((c-1)*50)+n;
if(last>h->sizearray)
luaH_resizearray(L,h,last);
for(;n>0;n--){
TValue*val=ra+n;
setobj(L,luaH_setnum(L,h,last--),val);
luaC_barriert(L,h,val);
}
continue;
}
case OP_CLOSE:{
luaF_close(L,ra);
continue;
}
case OP_CLOSURE:{
Proto*p;
Closure*ncl;
int nup,j;
p=cl->p->p[GETARG_Bx(i)];
nup=p->nups;
ncl=luaF_newLclosure(L,nup,cl->env);
ncl->l.p=p;
for(j=0;j<nup;j++,pc++){
if(GET_OPCODE(*pc)==OP_GETUPVAL)
ncl->l.upvals[j]=cl->upvals[GETARG_B(*pc)];
else{
ncl->l.upvals[j]=luaF_findupval(L,base+GETARG_B(*pc));
}
}
setclvalue(L,ra,ncl);
Protect(luaC_checkGC(L));
continue;
}
case OP_VARARG:{
int b=GETARG_B(i)-1;
int j;
CallInfo*ci=L->ci;
int n=cast_int(ci->base-ci->func)-cl->p->numparams-1;
if(b==(-1)){
Protect(luaD_checkstack(L,n));
ra=RA(i);
b=n;
L->top=ra+n;
}
for(j=0;j<b;j++){
if(j<n){
setobj(L,ra+j,ci->base-n+j);
}
else{
setnilvalue(ra+j);
}
}
continue;
}
}
}
}
#define api_checknelems(L,n)luai_apicheck(L,(n)<=(L->top-L->base))
#define api_checkvalidindex(L,i)luai_apicheck(L,(i)!=(&luaO_nilobject_))
#define api_incr_top(L){luai_apicheck(L,L->top<L->ci->top);L->top++;}
static TValue*index2adr(lua_State*L,int idx){
if(idx>0){
TValue*o=L->base+(idx-1);
luai_apicheck(L,idx<=L->ci->top-L->base);
if(o>=L->top)return cast(TValue*,(&luaO_nilobject_));
else return o;
}
else if(idx>(-10000)){
luai_apicheck(L,idx!=0&&-idx<=L->top-L->base);
return L->top+idx;
}
else switch(idx){
case(-10000):return registry(L);
case(-10001):{
Closure*func=curr_func(L);
sethvalue(L,&L->env,func->c.env);
return&L->env;
}
case(-10002):return gt(L);
default:{
Closure*func=curr_func(L);
idx=(-10002)-idx;
return(idx<=func->c.nupvalues)
?&func->c.upvalue[idx-1]
:cast(TValue*,(&luaO_nilobject_));
}
}
}
static Table*getcurrenv(lua_State*L){
if(L->ci==L->base_ci)
return hvalue(gt(L));
else{
Closure*func=curr_func(L);
return func->c.env;
}
}
static int lua_checkstack(lua_State*L,int size){
int res=1;
if(size>8000||(L->top-L->base+size)>8000)
res=0;
else if(size>0){
luaD_checkstack(L,size);
if(L->ci->top<L->top+size)
L->ci->top=L->top+size;
}
return res;
}
static lua_CFunction lua_atpanic(lua_State*L,lua_CFunction panicf){
lua_CFunction old;
old=G(L)->panic;
G(L)->panic=panicf;
return old;
}
static int lua_gettop(lua_State*L){
return cast_int(L->top-L->base);
}
static void lua_settop(lua_State*L,int idx){
if(idx>=0){
luai_apicheck(L,idx<=L->stack_last-L->base);
while(L->top<L->base+idx)
setnilvalue(L->top++);
L->top=L->base+idx;
}
else{
luai_apicheck(L,-(idx+1)<=(L->top-L->base));
L->top+=idx+1;
}
}
static void lua_remove(lua_State*L,int idx){
StkId p;
p=index2adr(L,idx);
api_checkvalidindex(L,p);
while(++p<L->top)setobj(L,p-1,p);
L->top--;
}
static void lua_insert(lua_State*L,int idx){
StkId p;
StkId q;
p=index2adr(L,idx);
api_checkvalidindex(L,p);
for(q=L->top;q>p;q--)setobj(L,q,q-1);
setobj(L,p,L->top);
}
static void lua_replace(lua_State*L,int idx){
StkId o;
if(idx==(-10001)&&L->ci==L->base_ci)
luaG_runerror(L,"no calling environment");
api_checknelems(L,1);
o=index2adr(L,idx);
api_checkvalidindex(L,o);
if(idx==(-10001)){
Closure*func=curr_func(L);
luai_apicheck(L,ttistable(L->top-1));
func->c.env=hvalue(L->top-1);
luaC_barrier(L,func,L->top-1);
}
else{
setobj(L,o,L->top-1);
if(idx<(-10002))
luaC_barrier(L,curr_func(L),L->top-1);
}
L->top--;
}
static void lua_pushvalue(lua_State*L,int idx){
setobj(L,L->top,index2adr(L,idx));
api_incr_top(L);
}
static int lua_type(lua_State*L,int idx){
StkId o=index2adr(L,idx);
return(o==(&luaO_nilobject_))?(-1):ttype(o);
}
static const char*lua_typename(lua_State*L,int t){
UNUSED(L);
return(t==(-1))?"no value":luaT_typenames[t];
}
static int lua_iscfunction(lua_State*L,int idx){
StkId o=index2adr(L,idx);
return iscfunction(o);
}
static int lua_isnumber(lua_State*L,int idx){
TValue n;
const TValue*o=index2adr(L,idx);
return tonumber(o,&n);
}
static int lua_isstring(lua_State*L,int idx){
int t=lua_type(L,idx);
return(t==4||t==3);
}
static int lua_rawequal(lua_State*L,int index1,int index2){
StkId o1=index2adr(L,index1);
StkId o2=index2adr(L,index2);
return(o1==(&luaO_nilobject_)||o2==(&luaO_nilobject_))?0
:luaO_rawequalObj(o1,o2);
}
static int lua_lessthan(lua_State*L,int index1,int index2){
StkId o1,o2;
int i;
o1=index2adr(L,index1);
o2=index2adr(L,index2);
i=(o1==(&luaO_nilobject_)||o2==(&luaO_nilobject_))?0
:luaV_lessthan(L,o1,o2);
return i;
}
static lua_Number lua_tonumber(lua_State*L,int idx){
TValue n;
const TValue*o=index2adr(L,idx);
if(tonumber(o,&n))
return nvalue(o);
else
return 0;
}
static lua_Integer lua_tointeger(lua_State*L,int idx){
TValue n;
const TValue*o=index2adr(L,idx);
if(tonumber(o,&n)){
lua_Integer res;
lua_Number num=nvalue(o);
lua_number2integer(res,num);
return res;
}
else
return 0;
}
static int lua_toboolean(lua_State*L,int idx){
const TValue*o=index2adr(L,idx);
return!l_isfalse(o);
}
static const char*lua_tolstring(lua_State*L,int idx,size_t*len){
StkId o=index2adr(L,idx);
if(!ttisstring(o)){
if(!luaV_tostring(L,o)){
if(len!=NULL)*len=0;
return NULL;
}
luaC_checkGC(L);
o=index2adr(L,idx);
}
if(len!=NULL)*len=tsvalue(o)->len;
return svalue(o);
}
static size_t lua_objlen(lua_State*L,int idx){
StkId o=index2adr(L,idx);
switch(ttype(o)){
case 4:return tsvalue(o)->len;
case 7:return uvalue(o)->len;
case 5:return luaH_getn(hvalue(o));
case 3:{
size_t l;
l=(luaV_tostring(L,o)?tsvalue(o)->len:0);
return l;
}
default:return 0;
}
}
static lua_CFunction lua_tocfunction(lua_State*L,int idx){
StkId o=index2adr(L,idx);
return(!iscfunction(o))?NULL:clvalue(o)->c.f;
}
static void*lua_touserdata(lua_State*L,int idx){
StkId o=index2adr(L,idx);
switch(ttype(o)){
case 7:return(rawuvalue(o)+1);
case 2:return pvalue(o);
default:return NULL;
}
}
static void lua_pushnil(lua_State*L){
setnilvalue(L->top);
api_incr_top(L);
}
static void lua_pushnumber(lua_State*L,lua_Number n){
setnvalue(L->top,n);
api_incr_top(L);
}
static void lua_pushinteger(lua_State*L,lua_Integer n){
setnvalue(L->top,cast_num(n));
api_incr_top(L);
}
static void lua_pushlstring(lua_State*L,const char*s,size_t len){
luaC_checkGC(L);
setsvalue(L,L->top,luaS_newlstr(L,s,len));
api_incr_top(L);
}
static void lua_pushstring(lua_State*L,const char*s){
if(s==NULL)
lua_pushnil(L);
else
lua_pushlstring(L,s,strlen(s));
}
static const char*lua_pushvfstring(lua_State*L,const char*fmt,
va_list argp){
const char*ret;
luaC_checkGC(L);
ret=luaO_pushvfstring(L,fmt,argp);
return ret;
}
static const char*lua_pushfstring(lua_State*L,const char*fmt,...){
const char*ret;
va_list argp;
luaC_checkGC(L);
va_start(argp,fmt);
ret=luaO_pushvfstring(L,fmt,argp);
va_end(argp);
return ret;
}
static void lua_pushcclosure(lua_State*L,lua_CFunction fn,int n){
Closure*cl;
luaC_checkGC(L);
api_checknelems(L,n);
cl=luaF_newCclosure(L,n,getcurrenv(L));
cl->c.f=fn;
L->top-=n;
while(n--)
setobj(L,&cl->c.upvalue[n],L->top+n);
setclvalue(L,L->top,cl);
api_incr_top(L);
}
static void lua_pushboolean(lua_State*L,int b){
setbvalue(L->top,(b!=0));
api_incr_top(L);
}
static int lua_pushthread(lua_State*L){
setthvalue(L,L->top,L);
api_incr_top(L);
return(G(L)->mainthread==L);
}
static void lua_gettable(lua_State*L,int idx){
StkId t;
t=index2adr(L,idx);
api_checkvalidindex(L,t);
luaV_gettable(L,t,L->top-1,L->top-1);
}
static void lua_getfield(lua_State*L,int idx,const char*k){
StkId t;
TValue key;
t=index2adr(L,idx);
api_checkvalidindex(L,t);
setsvalue(L,&key,luaS_new(L,k));
luaV_gettable(L,t,&key,L->top);
api_incr_top(L);
}
static void lua_rawget(lua_State*L,int idx){
StkId t;
t=index2adr(L,idx);
luai_apicheck(L,ttistable(t));
setobj(L,L->top-1,luaH_get(hvalue(t),L->top-1));
}
static void lua_rawgeti(lua_State*L,int idx,int n){
StkId o;
o=index2adr(L,idx);
luai_apicheck(L,ttistable(o));
setobj(L,L->top,luaH_getnum(hvalue(o),n));
api_incr_top(L);
}
static void lua_createtable(lua_State*L,int narray,int nrec){
luaC_checkGC(L);
sethvalue(L,L->top,luaH_new(L,narray,nrec));
api_incr_top(L);
}
static int lua_getmetatable(lua_State*L,int objindex){
const TValue*obj;
Table*mt=NULL;
int res;
obj=index2adr(L,objindex);
switch(ttype(obj)){
case 5:
mt=hvalue(obj)->metatable;
break;
case 7:
mt=uvalue(obj)->metatable;
break;
default:
mt=G(L)->mt[ttype(obj)];
break;
}
if(mt==NULL)
res=0;
else{
sethvalue(L,L->top,mt);
api_incr_top(L);
res=1;
}
return res;
}
static void lua_getfenv(lua_State*L,int idx){
StkId o;
o=index2adr(L,idx);
api_checkvalidindex(L,o);
switch(ttype(o)){
case 6:
sethvalue(L,L->top,clvalue(o)->c.env);
break;
case 7:
sethvalue(L,L->top,uvalue(o)->env);
break;
case 8:
setobj(L,L->top,gt(thvalue(o)));
break;
default:
setnilvalue(L->top);
break;
}
api_incr_top(L);
}
static void lua_settable(lua_State*L,int idx){
StkId t;
api_checknelems(L,2);
t=index2adr(L,idx);
api_checkvalidindex(L,t);
luaV_settable(L,t,L->top-2,L->top-1);
L->top-=2;
}
static void lua_setfield(lua_State*L,int idx,const char*k){
StkId t;
TValue key;
api_checknelems(L,1);
t=index2adr(L,idx);
api_checkvalidindex(L,t);
setsvalue(L,&key,luaS_new(L,k));
luaV_settable(L,t,&key,L->top-1);
L->top--;
}
static void lua_rawset(lua_State*L,int idx){
StkId t;
api_checknelems(L,2);
t=index2adr(L,idx);
luai_apicheck(L,ttistable(t));
setobj(L,luaH_set(L,hvalue(t),L->top-2),L->top-1);
luaC_barriert(L,hvalue(t),L->top-1);
L->top-=2;
}
static void lua_rawseti(lua_State*L,int idx,int n){
StkId o;
api_checknelems(L,1);
o=index2adr(L,idx);
luai_apicheck(L,ttistable(o));
setobj(L,luaH_setnum(L,hvalue(o),n),L->top-1);
luaC_barriert(L,hvalue(o),L->top-1);
L->top--;
}
static int lua_setmetatable(lua_State*L,int objindex){
TValue*obj;
Table*mt;
api_checknelems(L,1);
obj=index2adr(L,objindex);
api_checkvalidindex(L,obj);
if(ttisnil(L->top-1))
mt=NULL;
else{
luai_apicheck(L,ttistable(L->top-1));
mt=hvalue(L->top-1);
}
switch(ttype(obj)){
case 5:{
hvalue(obj)->metatable=mt;
if(mt)
luaC_objbarriert(L,hvalue(obj),mt);
break;
}
case 7:{
uvalue(obj)->metatable=mt;
if(mt)
luaC_objbarrier(L,rawuvalue(obj),mt);
break;
}
default:{
G(L)->mt[ttype(obj)]=mt;
break;
}
}
L->top--;
return 1;
}
static int lua_setfenv(lua_State*L,int idx){
StkId o;
int res=1;
api_checknelems(L,1);
o=index2adr(L,idx);
api_checkvalidindex(L,o);
luai_apicheck(L,ttistable(L->top-1));
switch(ttype(o)){
case 6:
clvalue(o)->c.env=hvalue(L->top-1);
break;
case 7:
uvalue(o)->env=hvalue(L->top-1);
break;
case 8:
sethvalue(L,gt(thvalue(o)),hvalue(L->top-1));
break;
default:
res=0;
break;
}
if(res)luaC_objbarrier(L,gcvalue(o),hvalue(L->top-1));
L->top--;
return res;
}
#define adjustresults(L,nres){if(nres==(-1)&&L->top>=L->ci->top)L->ci->top=L->top;}
#define checkresults(L,na,nr)luai_apicheck(L,(nr)==(-1)||(L->ci->top-L->top>=(nr)-(na)))
static void lua_call(lua_State*L,int nargs,int nresults){
StkId func;
api_checknelems(L,nargs+1);
checkresults(L,nargs,nresults);
func=L->top-(nargs+1);
luaD_call(L,func,nresults);
adjustresults(L,nresults);
}
struct CallS{
StkId func;
int nresults;
};
static void f_call(lua_State*L,void*ud){
struct CallS*c=cast(struct CallS*,ud);
luaD_call(L,c->func,c->nresults);
}
static int lua_pcall(lua_State*L,int nargs,int nresults,int errfunc){
struct CallS c;
int status;
ptrdiff_t func;
api_checknelems(L,nargs+1);
checkresults(L,nargs,nresults);
if(errfunc==0)
func=0;
else{
StkId o=index2adr(L,errfunc);
api_checkvalidindex(L,o);
func=savestack(L,o);
}
c.func=L->top-(nargs+1);
c.nresults=nresults;
status=luaD_pcall(L,f_call,&c,savestack(L,c.func),func);
adjustresults(L,nresults);
return status;
}
static int lua_load(lua_State*L,lua_Reader reader,void*data,
const char*chunkname){
ZIO z;
int status;
if(!chunkname)chunkname="?";
luaZ_init(L,&z,reader,data);
status=luaD_protectedparser(L,&z,chunkname);
return status;
}
static int lua_error(lua_State*L){
api_checknelems(L,1);
luaG_errormsg(L);
return 0;
}
static int lua_next(lua_State*L,int idx){
StkId t;
int more;
t=index2adr(L,idx);
luai_apicheck(L,ttistable(t));
more=luaH_next(L,hvalue(t),L->top-1);
if(more){
api_incr_top(L);
}
else
L->top-=1;
return more;
}
static void lua_concat(lua_State*L,int n){
api_checknelems(L,n);
if(n>=2){
luaC_checkGC(L);
luaV_concat(L,n,cast_int(L->top-L->base)-1);
L->top-=(n-1);
}
else if(n==0){
setsvalue(L,L->top,luaS_newlstr(L,"",0));
api_incr_top(L);
}
}
static void*lua_newuserdata(lua_State*L,size_t size){
Udata*u;
luaC_checkGC(L);
u=luaS_newudata(L,size,getcurrenv(L));
setuvalue(L,L->top,u);
api_incr_top(L);
return u+1;
}
#define luaL_getn(L,i)((int)lua_objlen(L,i))
#define luaL_setn(L,i,j)((void)0)
typedef struct luaL_Reg{
const char*name;
lua_CFunction func;
}luaL_Reg;
static void luaI_openlib(lua_State*L,const char*libname,
const luaL_Reg*l,int nup);
static int luaL_argerror(lua_State*L,int numarg,const char*extramsg);
static const char* luaL_checklstring(lua_State*L,int numArg,
size_t*l);
static const char* luaL_optlstring(lua_State*L,int numArg,
const char*def,size_t*l);
static lua_Integer luaL_checkinteger(lua_State*L,int numArg);
static lua_Integer luaL_optinteger(lua_State*L,int nArg,
lua_Integer def);
static int luaL_error(lua_State*L,const char*fmt,...);
static const char* luaL_findtable(lua_State*L,int idx,
const char*fname,int szhint);
#define luaL_argcheck(L,cond,numarg,extramsg)((void)((cond)||luaL_argerror(L,(numarg),(extramsg))))
#define luaL_checkstring(L,n)(luaL_checklstring(L,(n),NULL))
#define luaL_optstring(L,n,d)(luaL_optlstring(L,(n),(d),NULL))
#define luaL_checkint(L,n)((int)luaL_checkinteger(L,(n)))
#define luaL_optint(L,n,d)((int)luaL_optinteger(L,(n),(d)))
#define luaL_typename(L,i)lua_typename(L,lua_type(L,(i)))
#define luaL_getmetatable(L,n)(lua_getfield(L,(-10000),(n)))
#define luaL_opt(L,f,n,d)(lua_isnoneornil(L,(n))?(d):f(L,(n)))
typedef struct luaL_Buffer{
char*p;
int lvl;
lua_State*L;
char buffer[BUFSIZ];
}luaL_Buffer;
#define luaL_addchar(B,c)((void)((B)->p<((B)->buffer+BUFSIZ)||luaL_prepbuffer(B)),(*(B)->p++=(char)(c)))
#define luaL_addsize(B,n)((B)->p+=(n))
static char* luaL_prepbuffer(luaL_Buffer*B);
static int luaL_argerror(lua_State*L,int narg,const char*extramsg){
lua_Debug ar;
if(!lua_getstack(L,0,&ar))
return luaL_error(L,"bad argument #%d (%s)",narg,extramsg);
lua_getinfo(L,"n",&ar);
if(strcmp(ar.namewhat,"method")==0){
narg--;
if(narg==0)
return luaL_error(L,"calling "LUA_QL("%s")" on bad self (%s)",
ar.name,extramsg);
}
if(ar.name==NULL)
ar.name="?";
return luaL_error(L,"bad argument #%d to "LUA_QL("%s")" (%s)",
narg,ar.name,extramsg);
}
static int luaL_typerror(lua_State*L,int narg,const char*tname){
const char*msg=lua_pushfstring(L,"%s expected, got %s",
tname,luaL_typename(L,narg));
return luaL_argerror(L,narg,msg);
}
static void tag_error(lua_State*L,int narg,int tag){
luaL_typerror(L,narg,lua_typename(L,tag));
}
static void luaL_where(lua_State*L,int level){
lua_Debug ar;
if(lua_getstack(L,level,&ar)){
lua_getinfo(L,"Sl",&ar);
if(ar.currentline>0){
lua_pushfstring(L,"%s:%d: ",ar.short_src,ar.currentline);
return;
}
}
lua_pushliteral(L,"");
}
static int luaL_error(lua_State*L,const char*fmt,...){
va_list argp;
va_start(argp,fmt);
luaL_where(L,1);
lua_pushvfstring(L,fmt,argp);
va_end(argp);
lua_concat(L,2);
return lua_error(L);
}
static int luaL_newmetatable(lua_State*L,const char*tname){
lua_getfield(L,(-10000),tname);
if(!lua_isnil(L,-1))
return 0;
lua_pop(L,1);
lua_newtable(L);
lua_pushvalue(L,-1);
lua_setfield(L,(-10000),tname);
return 1;
}
static void*luaL_checkudata(lua_State*L,int ud,const char*tname){
void*p=lua_touserdata(L,ud);
if(p!=NULL){
if(lua_getmetatable(L,ud)){
lua_getfield(L,(-10000),tname);
if(lua_rawequal(L,-1,-2)){
lua_pop(L,2);
return p;
}
}
}
luaL_typerror(L,ud,tname);
return NULL;
}
static void luaL_checkstack(lua_State*L,int space,const char*mes){
if(!lua_checkstack(L,space))
luaL_error(L,"stack overflow (%s)",mes);
}
static void luaL_checktype(lua_State*L,int narg,int t){
if(lua_type(L,narg)!=t)
tag_error(L,narg,t);
}
static void luaL_checkany(lua_State*L,int narg){
if(lua_type(L,narg)==(-1))
luaL_argerror(L,narg,"value expected");
}
static const char*luaL_checklstring(lua_State*L,int narg,size_t*len){
const char*s=lua_tolstring(L,narg,len);
if(!s)tag_error(L,narg,4);
return s;
}
static const char*luaL_optlstring(lua_State*L,int narg,
const char*def,size_t*len){
if(lua_isnoneornil(L,narg)){
if(len)
*len=(def?strlen(def):0);
return def;
}
else return luaL_checklstring(L,narg,len);
}
static lua_Number luaL_checknumber(lua_State*L,int narg){
lua_Number d=lua_tonumber(L,narg);
if(d==0&&!lua_isnumber(L,narg))
tag_error(L,narg,3);
return d;
}
static lua_Integer luaL_checkinteger(lua_State*L,int narg){
lua_Integer d=lua_tointeger(L,narg);
if(d==0&&!lua_isnumber(L,narg))
tag_error(L,narg,3);
return d;
}
static lua_Integer luaL_optinteger(lua_State*L,int narg,
lua_Integer def){
return luaL_opt(L,luaL_checkinteger,narg,def);
}
static int luaL_getmetafield(lua_State*L,int obj,const char*event){
if(!lua_getmetatable(L,obj))
return 0;
lua_pushstring(L,event);
lua_rawget(L,-2);
if(lua_isnil(L,-1)){
lua_pop(L,2);
return 0;
}
else{
lua_remove(L,-2);
return 1;
}
}
static void luaL_register(lua_State*L,const char*libname,
const luaL_Reg*l){
luaI_openlib(L,libname,l,0);
}
static int libsize(const luaL_Reg*l){
int size=0;
for(;l->name;l++)size++;
return size;
}
static void luaI_openlib(lua_State*L,const char*libname,
const luaL_Reg*l,int nup){
if(libname){
int size=libsize(l);
luaL_findtable(L,(-10000),"_LOADED",1);
lua_getfield(L,-1,libname);
if(!lua_istable(L,-1)){
lua_pop(L,1);
if(luaL_findtable(L,(-10002),libname,size)!=NULL)
luaL_error(L,"name conflict for module "LUA_QL("%s"),libname);
lua_pushvalue(L,-1);
lua_setfield(L,-3,libname);
}
lua_remove(L,-2);
lua_insert(L,-(nup+1));
}
for(;l->name;l++){
int i;
for(i=0;i<nup;i++)
lua_pushvalue(L,-nup);
lua_pushcclosure(L,l->func,nup);
lua_setfield(L,-(nup+2),l->name);
}
lua_pop(L,nup);
}
static const char*luaL_findtable(lua_State*L,int idx,
const char*fname,int szhint){
const char*e;
lua_pushvalue(L,idx);
do{
e=strchr(fname,'.');
if(e==NULL)e=fname+strlen(fname);
lua_pushlstring(L,fname,e-fname);
lua_rawget(L,-2);
if(lua_isnil(L,-1)){
lua_pop(L,1);
lua_createtable(L,0,(*e=='.'?1:szhint));
lua_pushlstring(L,fname,e-fname);
lua_pushvalue(L,-2);
lua_settable(L,-4);
}
else if(!lua_istable(L,-1)){
lua_pop(L,2);
return fname;
}
lua_remove(L,-2);
fname=e+1;
}while(*e=='.');
return NULL;
}
#define bufflen(B)((B)->p-(B)->buffer)
#define bufffree(B)((size_t)(BUFSIZ-bufflen(B)))
static int emptybuffer(luaL_Buffer*B){
size_t l=bufflen(B);
if(l==0)return 0;
else{
lua_pushlstring(B->L,B->buffer,l);
B->p=B->buffer;
B->lvl++;
return 1;
}
}
static void adjuststack(luaL_Buffer*B){
if(B->lvl>1){
lua_State*L=B->L;
int toget=1;
size_t toplen=lua_strlen(L,-1);
do{
size_t l=lua_strlen(L,-(toget+1));
if(B->lvl-toget+1>=(20/2)||toplen>l){
toplen+=l;
toget++;
}
else break;
}while(toget<B->lvl);
lua_concat(L,toget);
B->lvl=B->lvl-toget+1;
}
}
static char*luaL_prepbuffer(luaL_Buffer*B){
if(emptybuffer(B))
adjuststack(B);
return B->buffer;
}
static void luaL_addlstring(luaL_Buffer*B,const char*s,size_t l){
while(l--)
luaL_addchar(B,*s++);
}
static void luaL_pushresult(luaL_Buffer*B){
emptybuffer(B);
lua_concat(B->L,B->lvl);
B->lvl=1;
}
static void luaL_addvalue(luaL_Buffer*B){
lua_State*L=B->L;
size_t vl;
const char*s=lua_tolstring(L,-1,&vl);
if(vl<=bufffree(B)){
memcpy(B->p,s,vl);
B->p+=vl;
lua_pop(L,1);
}
else{
if(emptybuffer(B))
lua_insert(L,-2);
B->lvl++;
adjuststack(B);
}
}
static void luaL_buffinit(lua_State*L,luaL_Buffer*B){
B->L=L;
B->p=B->buffer;
B->lvl=0;
}
typedef struct LoadF{
int extraline;
FILE*f;
char buff[BUFSIZ];
}LoadF;
static const char*getF(lua_State*L,void*ud,size_t*size){
LoadF*lf=(LoadF*)ud;
(void)L;
if(lf->extraline){
lf->extraline=0;
*size=1;
return"\n";
}
if(feof(lf->f))return NULL;
*size=fread(lf->buff,1,sizeof(lf->buff),lf->f);
return(*size>0)?lf->buff:NULL;
}
static int errfile(lua_State*L,const char*what,int fnameindex){
const char*serr=strerror(errno);
const char*filename=lua_tostring(L,fnameindex)+1;
lua_pushfstring(L,"cannot %s %s: %s",what,filename,serr);
lua_remove(L,fnameindex);
return(5+1);
}
static int luaL_loadfile(lua_State*L,const char*filename){
LoadF lf;
int status,readstatus;
int c;
int fnameindex=lua_gettop(L)+1;
lf.extraline=0;
if(filename==NULL){
lua_pushliteral(L,"=stdin");
lf.f=stdin;
}
else{
lua_pushfstring(L,"@%s",filename);
lf.f=fopen(filename,"r");
if(lf.f==NULL)return errfile(L,"open",fnameindex);
}
c=getc(lf.f);
if(c=='#'){
lf.extraline=1;
while((c=getc(lf.f))!=EOF&&c!='\n');
if(c=='\n')c=getc(lf.f);
}
if(c=="\033Lua"[0]&&filename){
lf.f=freopen(filename,"rb",lf.f);
if(lf.f==NULL)return errfile(L,"reopen",fnameindex);
while((c=getc(lf.f))!=EOF&&c!="\033Lua"[0]);
lf.extraline=0;
}
ungetc(c,lf.f);
status=lua_load(L,getF,&lf,lua_tostring(L,-1));
readstatus=ferror(lf.f);
if(filename)fclose(lf.f);
if(readstatus){
lua_settop(L,fnameindex);
return errfile(L,"read",fnameindex);
}
lua_remove(L,fnameindex);
return status;
}
typedef struct LoadS{
const char*s;
size_t size;
}LoadS;
static const char*getS(lua_State*L,void*ud,size_t*size){
LoadS*ls=(LoadS*)ud;
(void)L;
if(ls->size==0)return NULL;
*size=ls->size;
ls->size=0;
return ls->s;
}
static int luaL_loadbuffer(lua_State*L,const char*buff,size_t size,
const char*name){
LoadS ls;
ls.s=buff;
ls.size=size;
return lua_load(L,getS,&ls,name);
}
static void*l_alloc(void*ud,void*ptr,size_t osize,size_t nsize){
(void)ud;
(void)osize;
if(nsize==0){
free(ptr);
return NULL;
}
else
return realloc(ptr,nsize);
}
static int panic(lua_State*L){
(void)L;
fprintf(stderr,"PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L,-1));
return 0;
}
static lua_State*luaL_newstate(void){
lua_State*L=lua_newstate(l_alloc,NULL);
if(L)lua_atpanic(L,&panic);
return L;
}
static int luaB_tonumber(lua_State*L){
int base=luaL_optint(L,2,10);
if(base==10){
luaL_checkany(L,1);
if(lua_isnumber(L,1)){
lua_pushnumber(L,lua_tonumber(L,1));
return 1;
}
}
else{
const char*s1=luaL_checkstring(L,1);
char*s2;
unsigned long n;
luaL_argcheck(L,2<=base&&base<=36,2,"base out of range");
n=strtoul(s1,&s2,base);
if(s1!=s2){
while(isspace((unsigned char)(*s2)))s2++;
if(*s2=='\0'){
lua_pushnumber(L,(lua_Number)n);
return 1;
}
}
}
lua_pushnil(L);
return 1;
}
static int luaB_error(lua_State*L){
int level=luaL_optint(L,2,1);
lua_settop(L,1);
if(lua_isstring(L,1)&&level>0){
luaL_where(L,level);
lua_pushvalue(L,1);
lua_concat(L,2);
}
return lua_error(L);
}
static int luaB_setmetatable(lua_State*L){
int t=lua_type(L,2);
luaL_checktype(L,1,5);
luaL_argcheck(L,t==0||t==5,2,
"nil or table expected");
if(luaL_getmetafield(L,1,"__metatable"))
luaL_error(L,"cannot change a protected metatable");
lua_settop(L,2);
lua_setmetatable(L,1);
return 1;
}
static void getfunc(lua_State*L,int opt){
if(lua_isfunction(L,1))lua_pushvalue(L,1);
else{
lua_Debug ar;
int level=opt?luaL_optint(L,1,1):luaL_checkint(L,1);
luaL_argcheck(L,level>=0,1,"level must be non-negative");
if(lua_getstack(L,level,&ar)==0)
luaL_argerror(L,1,"invalid level");
lua_getinfo(L,"f",&ar);
if(lua_isnil(L,-1))
luaL_error(L,"no function environment for tail call at level %d",
level);
}
}
static int luaB_setfenv(lua_State*L){
luaL_checktype(L,2,5);
getfunc(L,0);
lua_pushvalue(L,2);
if(lua_isnumber(L,1)&&lua_tonumber(L,1)==0){
lua_pushthread(L);
lua_insert(L,-2);
lua_setfenv(L,-2);
return 0;
}
else if(lua_iscfunction(L,-2)||lua_setfenv(L,-2)==0)
luaL_error(L,
LUA_QL("setfenv")" cannot change environment of given object");
return 1;
}
static int luaB_rawget(lua_State*L){
luaL_checktype(L,1,5);
luaL_checkany(L,2);
lua_settop(L,2);
lua_rawget(L,1);
return 1;
}
static int luaB_type(lua_State*L){
luaL_checkany(L,1);
lua_pushstring(L,luaL_typename(L,1));
return 1;
}
static int luaB_next(lua_State*L){
luaL_checktype(L,1,5);
lua_settop(L,2);
if(lua_next(L,1))
return 2;
else{
lua_pushnil(L);
return 1;
}
}
static int luaB_pairs(lua_State*L){
luaL_checktype(L,1,5);
lua_pushvalue(L,lua_upvalueindex(1));
lua_pushvalue(L,1);
lua_pushnil(L);
return 3;
}
static int ipairsaux(lua_State*L){
int i=luaL_checkint(L,2);
luaL_checktype(L,1,5);
i++;
lua_pushinteger(L,i);
lua_rawgeti(L,1,i);
return(lua_isnil(L,-1))?0:2;
}
static int luaB_ipairs(lua_State*L){
luaL_checktype(L,1,5);
lua_pushvalue(L,lua_upvalueindex(1));
lua_pushvalue(L,1);
lua_pushinteger(L,0);
return 3;
}
static int load_aux(lua_State*L,int status){
if(status==0)
return 1;
else{
lua_pushnil(L);
lua_insert(L,-2);
return 2;
}
}
static int luaB_loadstring(lua_State*L){
size_t l;
const char*s=luaL_checklstring(L,1,&l);
const char*chunkname=luaL_optstring(L,2,s);
return load_aux(L,luaL_loadbuffer(L,s,l,chunkname));
}
static int luaB_loadfile(lua_State*L){
const char*fname=luaL_optstring(L,1,NULL);
return load_aux(L,luaL_loadfile(L,fname));
}
static int luaB_assert(lua_State*L){
luaL_checkany(L,1);
if(!lua_toboolean(L,1))
return luaL_error(L,"%s",luaL_optstring(L,2,"assertion failed!"));
return lua_gettop(L);
}
static int luaB_unpack(lua_State*L){
int i,e,n;
luaL_checktype(L,1,5);
i=luaL_optint(L,2,1);
e=luaL_opt(L,luaL_checkint,3,luaL_getn(L,1));
if(i>e)return 0;
n=e-i+1;
if(n<=0||!lua_checkstack(L,n))
return luaL_error(L,"too many results to unpack");
lua_rawgeti(L,1,i);
while(i++<e)
lua_rawgeti(L,1,i);
return n;
}
static int luaB_pcall(lua_State*L){
int status;
luaL_checkany(L,1);
status=lua_pcall(L,lua_gettop(L)-1,(-1),0);
lua_pushboolean(L,(status==0));
lua_insert(L,1);
return lua_gettop(L);
}
static int luaB_newproxy(lua_State*L){
lua_settop(L,1);
lua_newuserdata(L,0);
if(lua_toboolean(L,1)==0)
return 1;
else if(lua_isboolean(L,1)){
lua_newtable(L);
lua_pushvalue(L,-1);
lua_pushboolean(L,1);
lua_rawset(L,lua_upvalueindex(1));
}
else{
int validproxy=0;
if(lua_getmetatable(L,1)){
lua_rawget(L,lua_upvalueindex(1));
validproxy=lua_toboolean(L,-1);
lua_pop(L,1);
}
luaL_argcheck(L,validproxy,1,"boolean or proxy expected");
lua_getmetatable(L,1);
}
lua_setmetatable(L,2);
return 1;
}
static const luaL_Reg base_funcs[]={
{"assert",luaB_assert},
{"error",luaB_error},
{"loadfile",luaB_loadfile},
{"loadstring",luaB_loadstring},
{"next",luaB_next},
{"pcall",luaB_pcall},
{"rawget",luaB_rawget},
{"setfenv",luaB_setfenv},
{"setmetatable",luaB_setmetatable},
{"tonumber",luaB_tonumber},
{"type",luaB_type},
{"unpack",luaB_unpack},
{NULL,NULL}
};
static void auxopen(lua_State*L,const char*name,
lua_CFunction f,lua_CFunction u){
lua_pushcfunction(L,u);
lua_pushcclosure(L,f,1);
lua_setfield(L,-2,name);
}
static void base_open(lua_State*L){
lua_pushvalue(L,(-10002));
lua_setglobal(L,"_G");
luaL_register(L,"_G",base_funcs);
lua_pushliteral(L,"Lua 5.1");
lua_setglobal(L,"_VERSION");
auxopen(L,"ipairs",luaB_ipairs,ipairsaux);
auxopen(L,"pairs",luaB_pairs,luaB_next);
lua_createtable(L,0,1);
lua_pushvalue(L,-1);
lua_setmetatable(L,-2);
lua_pushliteral(L,"kv");
lua_setfield(L,-2,"__mode");
lua_pushcclosure(L,luaB_newproxy,1);
lua_setglobal(L,"newproxy");
}
static int luaopen_base(lua_State*L){
base_open(L);
return 1;
}
#define aux_getn(L,n)(luaL_checktype(L,n,5),luaL_getn(L,n))
static int tinsert(lua_State*L){
int e=aux_getn(L,1)+1;
int pos;
switch(lua_gettop(L)){
case 2:{
pos=e;
break;
}
case 3:{
int i;
pos=luaL_checkint(L,2);
if(pos>e)e=pos;
for(i=e;i>pos;i--){
lua_rawgeti(L,1,i-1);
lua_rawseti(L,1,i);
}
break;
}
default:{
return luaL_error(L,"wrong number of arguments to "LUA_QL("insert"));
}
}
luaL_setn(L,1,e);
lua_rawseti(L,1,pos);
return 0;
}
static int tremove(lua_State*L){
int e=aux_getn(L,1);
int pos=luaL_optint(L,2,e);
if(!(1<=pos&&pos<=e))
return 0;
luaL_setn(L,1,e-1);
lua_rawgeti(L,1,pos);
for(;pos<e;pos++){
lua_rawgeti(L,1,pos+1);
lua_rawseti(L,1,pos);
}
lua_pushnil(L);
lua_rawseti(L,1,e);
return 1;
}
static void addfield(lua_State*L,luaL_Buffer*b,int i){
lua_rawgeti(L,1,i);
if(!lua_isstring(L,-1))
luaL_error(L,"invalid value (%s) at index %d in table for "
LUA_QL("concat"),luaL_typename(L,-1),i);
luaL_addvalue(b);
}
static int tconcat(lua_State*L){
luaL_Buffer b;
size_t lsep;
int i,last;
const char*sep=luaL_optlstring(L,2,"",&lsep);
luaL_checktype(L,1,5);
i=luaL_optint(L,3,1);
last=luaL_opt(L,luaL_checkint,4,luaL_getn(L,1));
luaL_buffinit(L,&b);
for(;i<last;i++){
addfield(L,&b,i);
luaL_addlstring(&b,sep,lsep);
}
if(i==last)
addfield(L,&b,i);
luaL_pushresult(&b);
return 1;
}
static void set2(lua_State*L,int i,int j){
lua_rawseti(L,1,i);
lua_rawseti(L,1,j);
}
static int sort_comp(lua_State*L,int a,int b){
if(!lua_isnil(L,2)){
int res;
lua_pushvalue(L,2);
lua_pushvalue(L,a-1);
lua_pushvalue(L,b-2);
lua_call(L,2,1);
res=lua_toboolean(L,-1);
lua_pop(L,1);
return res;
}
else
return lua_lessthan(L,a,b);
}
static void auxsort(lua_State*L,int l,int u){
while(l<u){
int i,j;
lua_rawgeti(L,1,l);
lua_rawgeti(L,1,u);
if(sort_comp(L,-1,-2))
set2(L,l,u);
else
lua_pop(L,2);
if(u-l==1)break;
i=(l+u)/2;
lua_rawgeti(L,1,i);
lua_rawgeti(L,1,l);
if(sort_comp(L,-2,-1))
set2(L,i,l);
else{
lua_pop(L,1);
lua_rawgeti(L,1,u);
if(sort_comp(L,-1,-2))
set2(L,i,u);
else
lua_pop(L,2);
}
if(u-l==2)break;
lua_rawgeti(L,1,i);
lua_pushvalue(L,-1);
lua_rawgeti(L,1,u-1);
set2(L,i,u-1);
i=l;j=u-1;
for(;;){
while(lua_rawgeti(L,1,++i),sort_comp(L,-1,-2)){
if(i>u)luaL_error(L,"invalid order function for sorting");
lua_pop(L,1);
}
while(lua_rawgeti(L,1,--j),sort_comp(L,-3,-1)){
if(j<l)luaL_error(L,"invalid order function for sorting");
lua_pop(L,1);
}
if(j<i){
lua_pop(L,3);
break;
}
set2(L,i,j);
}
lua_rawgeti(L,1,u-1);
lua_rawgeti(L,1,i);
set2(L,u-1,i);
if(i-l<u-i){
j=l;i=i-1;l=i+2;
}
else{
j=i+1;i=u;u=j-2;
}
auxsort(L,j,i);
}
}
static int sort(lua_State*L){
int n=aux_getn(L,1);
luaL_checkstack(L,40,"");
if(!lua_isnoneornil(L,2))
luaL_checktype(L,2,6);
lua_settop(L,2);
auxsort(L,1,n);
return 0;
}
static const luaL_Reg tab_funcs[]={
{"concat",tconcat},
{"insert",tinsert},
{"remove",tremove},
{"sort",sort},
{NULL,NULL}
};
static int luaopen_table(lua_State*L){
luaL_register(L,"table",tab_funcs);
return 1;
}
static const char*const fnames[]={"input","output"};
static int pushresult(lua_State*L,int i,const char*filename){
int en=errno;
if(i){
lua_pushboolean(L,1);
return 1;
}
else{
lua_pushnil(L);
if(filename)
lua_pushfstring(L,"%s: %s",filename,strerror(en));
else
lua_pushfstring(L,"%s",strerror(en));
lua_pushinteger(L,en);
return 3;
}
}
static void fileerror(lua_State*L,int arg,const char*filename){
lua_pushfstring(L,"%s: %s",filename,strerror(errno));
luaL_argerror(L,arg,lua_tostring(L,-1));
}
#define tofilep(L)((FILE**)luaL_checkudata(L,1,"FILE*"))
static int io_type(lua_State*L){
void*ud;
luaL_checkany(L,1);
ud=lua_touserdata(L,1);
lua_getfield(L,(-10000),"FILE*");
if(ud==NULL||!lua_getmetatable(L,1)||!lua_rawequal(L,-2,-1))
lua_pushnil(L);
else if(*((FILE**)ud)==NULL)
lua_pushliteral(L,"closed file");
else
lua_pushliteral(L,"file");
return 1;
}
static FILE*tofile(lua_State*L){
FILE**f=tofilep(L);
if(*f==NULL)
luaL_error(L,"attempt to use a closed file");
return*f;
}
static FILE**newfile(lua_State*L){
FILE**pf=(FILE**)lua_newuserdata(L,sizeof(FILE*));
*pf=NULL;
luaL_getmetatable(L,"FILE*");
lua_setmetatable(L,-2);
return pf;
}
static int io_noclose(lua_State*L){
lua_pushnil(L);
lua_pushliteral(L,"cannot close standard file");
return 2;
}
static int io_pclose(lua_State*L){
FILE**p=tofilep(L);
int ok=lua_pclose(L,*p);
*p=NULL;
return pushresult(L,ok,NULL);
}
static int io_fclose(lua_State*L){
FILE**p=tofilep(L);
int ok=(fclose(*p)==0);
*p=NULL;
return pushresult(L,ok,NULL);
}
static int aux_close(lua_State*L){
lua_getfenv(L,1);
lua_getfield(L,-1,"__close");
return(lua_tocfunction(L,-1))(L);
}
static int io_close(lua_State*L){
if(lua_isnone(L,1))
lua_rawgeti(L,(-10001),2);
tofile(L);
return aux_close(L);
}
static int io_gc(lua_State*L){
FILE*f=*tofilep(L);
if(f!=NULL)
aux_close(L);
return 0;
}
static int io_open(lua_State*L){
const char*filename=luaL_checkstring(L,1);
const char*mode=luaL_optstring(L,2,"r");
FILE**pf=newfile(L);
*pf=fopen(filename,mode);
return(*pf==NULL)?pushresult(L,0,filename):1;
}
static FILE*getiofile(lua_State*L,int findex){
FILE*f;
lua_rawgeti(L,(-10001),findex);
f=*(FILE**)lua_touserdata(L,-1);
if(f==NULL)
luaL_error(L,"standard %s file is closed",fnames[findex-1]);
return f;
}
static int g_iofile(lua_State*L,int f,const char*mode){
if(!lua_isnoneornil(L,1)){
const char*filename=lua_tostring(L,1);
if(filename){
FILE**pf=newfile(L);
*pf=fopen(filename,mode);
if(*pf==NULL)
fileerror(L,1,filename);
}
else{
tofile(L);
lua_pushvalue(L,1);
}
lua_rawseti(L,(-10001),f);
}
lua_rawgeti(L,(-10001),f);
return 1;
}
static int io_input(lua_State*L){
return g_iofile(L,1,"r");
}
static int io_output(lua_State*L){
return g_iofile(L,2,"w");
}
static int io_readline(lua_State*L);
static void aux_lines(lua_State*L,int idx,int toclose){
lua_pushvalue(L,idx);
lua_pushboolean(L,toclose);
lua_pushcclosure(L,io_readline,2);
}
static int f_lines(lua_State*L){
tofile(L);
aux_lines(L,1,0);
return 1;
}
static int io_lines(lua_State*L){
if(lua_isnoneornil(L,1)){
lua_rawgeti(L,(-10001),1);
return f_lines(L);
}
else{
const char*filename=luaL_checkstring(L,1);
FILE**pf=newfile(L);
*pf=fopen(filename,"r");
if(*pf==NULL)
fileerror(L,1,filename);
aux_lines(L,lua_gettop(L),1);
return 1;
}
}
static int read_number(lua_State*L,FILE*f){
lua_Number d;
if(fscanf(f,"%lf",&d)==1){
lua_pushnumber(L,d);
return 1;
}
else{
lua_pushnil(L);
return 0;
}
}
static int test_eof(lua_State*L,FILE*f){
int c=getc(f);
ungetc(c,f);
lua_pushlstring(L,NULL,0);
return(c!=EOF);
}
static int read_line(lua_State*L,FILE*f){
luaL_Buffer b;
luaL_buffinit(L,&b);
for(;;){
size_t l;
char*p=luaL_prepbuffer(&b);
if(fgets(p,BUFSIZ,f)==NULL){
luaL_pushresult(&b);
return(lua_objlen(L,-1)>0);
}
l=strlen(p);
if(l==0||p[l-1]!='\n')
luaL_addsize(&b,l);
else{
luaL_addsize(&b,l-1);
luaL_pushresult(&b);
return 1;
}
}
}
static int read_chars(lua_State*L,FILE*f,size_t n){
size_t rlen;
size_t nr;
luaL_Buffer b;
luaL_buffinit(L,&b);
rlen=BUFSIZ;
do{
char*p=luaL_prepbuffer(&b);
if(rlen>n)rlen=n;
nr=fread(p,sizeof(char),rlen,f);
luaL_addsize(&b,nr);
n-=nr;
}while(n>0&&nr==rlen);
luaL_pushresult(&b);
return(n==0||lua_objlen(L,-1)>0);
}
static int g_read(lua_State*L,FILE*f,int first){
int nargs=lua_gettop(L)-1;
int success;
int n;
clearerr(f);
if(nargs==0){
success=read_line(L,f);
n=first+1;
}
else{
luaL_checkstack(L,nargs+20,"too many arguments");
success=1;
for(n=first;nargs--&&success;n++){
if(lua_type(L,n)==3){
size_t l=(size_t)lua_tointeger(L,n);
success=(l==0)?test_eof(L,f):read_chars(L,f,l);
}
else{
const char*p=lua_tostring(L,n);
luaL_argcheck(L,p&&p[0]=='*',n,"invalid option");
switch(p[1]){
case'n':
success=read_number(L,f);
break;
case'l':
success=read_line(L,f);
break;
case'a':
read_chars(L,f,~((size_t)0));
success=1;
break;
default:
return luaL_argerror(L,n,"invalid format");
}
}
}
}
if(ferror(f))
return pushresult(L,0,NULL);
if(!success){
lua_pop(L,1);
lua_pushnil(L);
}
return n-first;
}
static int io_read(lua_State*L){
return g_read(L,getiofile(L,1),1);
}
static int f_read(lua_State*L){
return g_read(L,tofile(L),2);
}
static int io_readline(lua_State*L){
FILE*f=*(FILE**)lua_touserdata(L,lua_upvalueindex(1));
int sucess;
if(f==NULL)
luaL_error(L,"file is already closed");
sucess=read_line(L,f);
if(ferror(f))
return luaL_error(L,"%s",strerror(errno));
if(sucess)return 1;
else{
if(lua_toboolean(L,lua_upvalueindex(2))){
lua_settop(L,0);
lua_pushvalue(L,lua_upvalueindex(1));
aux_close(L);
}
return 0;
}
}
static int g_write(lua_State*L,FILE*f,int arg){
int nargs=lua_gettop(L)-1;
int status=1;
for(;nargs--;arg++){
if(lua_type(L,arg)==3){
status=status&&
fprintf(f,"%.14g",lua_tonumber(L,arg))>0;
}
else{
size_t l;
const char*s=luaL_checklstring(L,arg,&l);
status=status&&(fwrite(s,sizeof(char),l,f)==l);
}
}
return pushresult(L,status,NULL);
}
static int io_write(lua_State*L){
return g_write(L,getiofile(L,2),1);
}
static int f_write(lua_State*L){
return g_write(L,tofile(L),2);
}
static int io_flush(lua_State*L){
return pushresult(L,fflush(getiofile(L,2))==0,NULL);
}
static int f_flush(lua_State*L){
return pushresult(L,fflush(tofile(L))==0,NULL);
}
static const luaL_Reg iolib[]={
{"close",io_close},
{"flush",io_flush},
{"input",io_input},
{"lines",io_lines},
{"open",io_open},
{"output",io_output},
{"read",io_read},
{"type",io_type},
{"write",io_write},
{NULL,NULL}
};
static const luaL_Reg flib[]={
{"close",io_close},
{"flush",f_flush},
{"lines",f_lines},
{"read",f_read},
{"write",f_write},
{"__gc",io_gc},
{NULL,NULL}
};
static void createmeta(lua_State*L){
luaL_newmetatable(L,"FILE*");
lua_pushvalue(L,-1);
lua_setfield(L,-2,"__index");
luaL_register(L,NULL,flib);
}
static void createstdfile(lua_State*L,FILE*f,int k,const char*fname){
*newfile(L)=f;
if(k>0){
lua_pushvalue(L,-1);
lua_rawseti(L,(-10001),k);
}
lua_pushvalue(L,-2);
lua_setfenv(L,-2);
lua_setfield(L,-3,fname);
}
static void newfenv(lua_State*L,lua_CFunction cls){
lua_createtable(L,0,1);
lua_pushcfunction(L,cls);
lua_setfield(L,-2,"__close");
}
static int luaopen_io(lua_State*L){
createmeta(L);
newfenv(L,io_fclose);
lua_replace(L,(-10001));
luaL_register(L,"io",iolib);
newfenv(L,io_noclose);
createstdfile(L,stdin,1,"stdin");
createstdfile(L,stdout,2,"stdout");
createstdfile(L,stderr,0,"stderr");
lua_pop(L,1);
lua_getfield(L,-1,"popen");
newfenv(L,io_pclose);
lua_setfenv(L,-2);
lua_pop(L,1);
return 1;
}
static int os_pushresult(lua_State*L,int i,const char*filename){
int en=errno;
if(i){
lua_pushboolean(L,1);
return 1;
}
else{
lua_pushnil(L);
lua_pushfstring(L,"%s: %s",filename,strerror(en));
lua_pushinteger(L,en);
return 3;
}
}
static int os_remove(lua_State*L){
const char*filename=luaL_checkstring(L,1);
return os_pushresult(L,remove(filename)==0,filename);
}
static int os_exit(lua_State*L){
exit(luaL_optint(L,1,EXIT_SUCCESS));
}
static const luaL_Reg syslib[]={
{"exit",os_exit},
{"remove",os_remove},
{NULL,NULL}
};
static int luaopen_os(lua_State*L){
luaL_register(L,"os",syslib);
return 1;
}
#define uchar(c)((unsigned char)(c))
static ptrdiff_t posrelat(ptrdiff_t pos,size_t len){
if(pos<0)pos+=(ptrdiff_t)len+1;
return(pos>=0)?pos:0;
}
static int str_sub(lua_State*L){
size_t l;
const char*s=luaL_checklstring(L,1,&l);
ptrdiff_t start=posrelat(luaL_checkinteger(L,2),l);
ptrdiff_t end=posrelat(luaL_optinteger(L,3,-1),l);
if(start<1)start=1;
if(end>(ptrdiff_t)l)end=(ptrdiff_t)l;
if(start<=end)
lua_pushlstring(L,s+start-1,end-start+1);
else lua_pushliteral(L,"");
return 1;
}
static int str_lower(lua_State*L){
size_t l;
size_t i;
luaL_Buffer b;
const char*s=luaL_checklstring(L,1,&l);
luaL_buffinit(L,&b);
for(i=0;i<l;i++)
luaL_addchar(&b,tolower(uchar(s[i])));
luaL_pushresult(&b);
return 1;
}
static int str_upper(lua_State*L){
size_t l;
size_t i;
luaL_Buffer b;
const char*s=luaL_checklstring(L,1,&l);
luaL_buffinit(L,&b);
for(i=0;i<l;i++)
luaL_addchar(&b,toupper(uchar(s[i])));
luaL_pushresult(&b);
return 1;
}
static int str_rep(lua_State*L){
size_t l;
luaL_Buffer b;
const char*s=luaL_checklstring(L,1,&l);
int n=luaL_checkint(L,2);
luaL_buffinit(L,&b);
while(n-->0)
luaL_addlstring(&b,s,l);
luaL_pushresult(&b);
return 1;
}
static int str_byte(lua_State*L){
size_t l;
const char*s=luaL_checklstring(L,1,&l);
ptrdiff_t posi=posrelat(luaL_optinteger(L,2,1),l);
ptrdiff_t pose=posrelat(luaL_optinteger(L,3,posi),l);
int n,i;
if(posi<=0)posi=1;
if((size_t)pose>l)pose=l;
if(posi>pose)return 0;
n=(int)(pose-posi+1);
if(posi+n<=pose)
luaL_error(L,"string slice too long");
luaL_checkstack(L,n,"string slice too long");
for(i=0;i<n;i++)
lua_pushinteger(L,uchar(s[posi+i-1]));
return n;
}
static int str_char(lua_State*L){
int n=lua_gettop(L);
int i;
luaL_Buffer b;
luaL_buffinit(L,&b);
for(i=1;i<=n;i++){
int c=luaL_checkint(L,i);
luaL_argcheck(L,uchar(c)==c,i,"invalid value");
luaL_addchar(&b,uchar(c));
}
luaL_pushresult(&b);
return 1;
}
typedef struct MatchState{
const char*src_init;
const char*src_end;
lua_State*L;
int level;
struct{
const char*init;
ptrdiff_t len;
}capture[32];
}MatchState;
static int check_capture(MatchState*ms,int l){
l-='1';
if(l<0||l>=ms->level||ms->capture[l].len==(-1))
return luaL_error(ms->L,"invalid capture index");
return l;
}
static int capture_to_close(MatchState*ms){
int level=ms->level;
for(level--;level>=0;level--)
if(ms->capture[level].len==(-1))return level;
return luaL_error(ms->L,"invalid pattern capture");
}
static const char*classend(MatchState*ms,const char*p){
switch(*p++){
case'%':{
if(*p=='\0')
luaL_error(ms->L,"malformed pattern (ends with "LUA_QL("%%")")");
return p+1;
}
case'[':{
if(*p=='^')p++;
do{
if(*p=='\0')
luaL_error(ms->L,"malformed pattern (missing "LUA_QL("]")")");
if(*(p++)=='%'&&*p!='\0')
p++;
}while(*p!=']');
return p+1;
}
default:{
return p;
}
}
}
static int match_class(int c,int cl){
int res;
switch(tolower(cl)){
case'a':res=isalpha(c);break;
case'c':res=iscntrl(c);break;
case'd':res=isdigit(c);break;
case'l':res=islower(c);break;
case'p':res=ispunct(c);break;
case's':res=isspace(c);break;
case'u':res=isupper(c);break;
case'w':res=isalnum(c);break;
case'x':res=isxdigit(c);break;
case'z':res=(c==0);break;
default:return(cl==c);
}
return(islower(cl)?res:!res);
}
static int matchbracketclass(int c,const char*p,const char*ec){
int sig=1;
if(*(p+1)=='^'){
sig=0;
p++;
}
while(++p<ec){
if(*p=='%'){
p++;
if(match_class(c,uchar(*p)))
return sig;
}
else if((*(p+1)=='-')&&(p+2<ec)){
p+=2;
if(uchar(*(p-2))<=c&&c<=uchar(*p))
return sig;
}
else if(uchar(*p)==c)return sig;
}
return!sig;
}
static int singlematch(int c,const char*p,const char*ep){
switch(*p){
case'.':return 1;
case'%':return match_class(c,uchar(*(p+1)));
case'[':return matchbracketclass(c,p,ep-1);
default:return(uchar(*p)==c);
}
}
static const char*match(MatchState*ms,const char*s,const char*p);
static const char*matchbalance(MatchState*ms,const char*s,
const char*p){
if(*p==0||*(p+1)==0)
luaL_error(ms->L,"unbalanced pattern");
if(*s!=*p)return NULL;
else{
int b=*p;
int e=*(p+1);
int cont=1;
while(++s<ms->src_end){
if(*s==e){
if(--cont==0)return s+1;
}
else if(*s==b)cont++;
}
}
return NULL;
}
static const char*max_expand(MatchState*ms,const char*s,
const char*p,const char*ep){
ptrdiff_t i=0;
while((s+i)<ms->src_end&&singlematch(uchar(*(s+i)),p,ep))
i++;
while(i>=0){
const char*res=match(ms,(s+i),ep+1);
if(res)return res;
i--;
}
return NULL;
}
static const char*min_expand(MatchState*ms,const char*s,
const char*p,const char*ep){
for(;;){
const char*res=match(ms,s,ep+1);
if(res!=NULL)
return res;
else if(s<ms->src_end&&singlematch(uchar(*s),p,ep))
s++;
else return NULL;
}
}
static const char*start_capture(MatchState*ms,const char*s,
const char*p,int what){
const char*res;
int level=ms->level;
if(level>=32)luaL_error(ms->L,"too many captures");
ms->capture[level].init=s;
ms->capture[level].len=what;
ms->level=level+1;
if((res=match(ms,s,p))==NULL)
ms->level--;
return res;
}
static const char*end_capture(MatchState*ms,const char*s,
const char*p){
int l=capture_to_close(ms);
const char*res;
ms->capture[l].len=s-ms->capture[l].init;
if((res=match(ms,s,p))==NULL)
ms->capture[l].len=(-1);
return res;
}
static const char*match_capture(MatchState*ms,const char*s,int l){
size_t len;
l=check_capture(ms,l);
len=ms->capture[l].len;
if((size_t)(ms->src_end-s)>=len&&
memcmp(ms->capture[l].init,s,len)==0)
return s+len;
else return NULL;
}
static const char*match(MatchState*ms,const char*s,const char*p){
init:
switch(*p){
case'(':{
if(*(p+1)==')')
return start_capture(ms,s,p+2,(-2));
else
return start_capture(ms,s,p+1,(-1));
}
case')':{
return end_capture(ms,s,p+1);
}
case'%':{
switch(*(p+1)){
case'b':{
s=matchbalance(ms,s,p+2);
if(s==NULL)return NULL;
p+=4;goto init;
}
case'f':{
const char*ep;char previous;
p+=2;
if(*p!='[')
luaL_error(ms->L,"missing "LUA_QL("[")" after "
LUA_QL("%%f")" in pattern");
ep=classend(ms,p);
previous=(s==ms->src_init)?'\0':*(s-1);
if(matchbracketclass(uchar(previous),p,ep-1)||
!matchbracketclass(uchar(*s),p,ep-1))return NULL;
p=ep;goto init;
}
default:{
if(isdigit(uchar(*(p+1)))){
s=match_capture(ms,s,uchar(*(p+1)));
if(s==NULL)return NULL;
p+=2;goto init;
}
goto dflt;
}
}
}
case'\0':{
return s;
}
case'$':{
if(*(p+1)=='\0')
return(s==ms->src_end)?s:NULL;
else goto dflt;
}
default:dflt:{
const char*ep=classend(ms,p);
int m=s<ms->src_end&&singlematch(uchar(*s),p,ep);
switch(*ep){
case'?':{
const char*res;
if(m&&((res=match(ms,s+1,ep+1))!=NULL))
return res;
p=ep+1;goto init;
}
case'*':{
return max_expand(ms,s,p,ep);
}
case'+':{
return(m?max_expand(ms,s+1,p,ep):NULL);
}
case'-':{
return min_expand(ms,s,p,ep);
}
default:{
if(!m)return NULL;
s++;p=ep;goto init;
}
}
}
}
}
static const char*lmemfind(const char*s1,size_t l1,
const char*s2,size_t l2){
if(l2==0)return s1;
else if(l2>l1)return NULL;
else{
const char*init;
l2--;
l1=l1-l2;
while(l1>0&&(init=(const char*)memchr(s1,*s2,l1))!=NULL){
init++;
if(memcmp(init,s2+1,l2)==0)
return init-1;
else{
l1-=init-s1;
s1=init;
}
}
return NULL;
}
}
static void push_onecapture(MatchState*ms,int i,const char*s,
const char*e){
if(i>=ms->level){
if(i==0)
lua_pushlstring(ms->L,s,e-s);
else
luaL_error(ms->L,"invalid capture index");
}
else{
ptrdiff_t l=ms->capture[i].len;
if(l==(-1))luaL_error(ms->L,"unfinished capture");
if(l==(-2))
lua_pushinteger(ms->L,ms->capture[i].init-ms->src_init+1);
else
lua_pushlstring(ms->L,ms->capture[i].init,l);
}
}
static int push_captures(MatchState*ms,const char*s,const char*e){
int i;
int nlevels=(ms->level==0&&s)?1:ms->level;
luaL_checkstack(ms->L,nlevels,"too many captures");
for(i=0;i<nlevels;i++)
push_onecapture(ms,i,s,e);
return nlevels;
}
static int str_find_aux(lua_State*L,int find){
size_t l1,l2;
const char*s=luaL_checklstring(L,1,&l1);
const char*p=luaL_checklstring(L,2,&l2);
ptrdiff_t init=posrelat(luaL_optinteger(L,3,1),l1)-1;
if(init<0)init=0;
else if((size_t)(init)>l1)init=(ptrdiff_t)l1;
if(find&&(lua_toboolean(L,4)||
strpbrk(p,"^$*+?.([%-")==NULL)){
const char*s2=lmemfind(s+init,l1-init,p,l2);
if(s2){
lua_pushinteger(L,s2-s+1);
lua_pushinteger(L,s2-s+l2);
return 2;
}
}
else{
MatchState ms;
int anchor=(*p=='^')?(p++,1):0;
const char*s1=s+init;
ms.L=L;
ms.src_init=s;
ms.src_end=s+l1;
do{
const char*res;
ms.level=0;
if((res=match(&ms,s1,p))!=NULL){
if(find){
lua_pushinteger(L,s1-s+1);
lua_pushinteger(L,res-s);
return push_captures(&ms,NULL,0)+2;
}
else
return push_captures(&ms,s1,res);
}
}while(s1++<ms.src_end&&!anchor);
}
lua_pushnil(L);
return 1;
}
static int str_find(lua_State*L){
return str_find_aux(L,1);
}
static int str_match(lua_State*L){
return str_find_aux(L,0);
}
static int gmatch_aux(lua_State*L){
MatchState ms;
size_t ls;
const char*s=lua_tolstring(L,lua_upvalueindex(1),&ls);
const char*p=lua_tostring(L,lua_upvalueindex(2));
const char*src;
ms.L=L;
ms.src_init=s;
ms.src_end=s+ls;
for(src=s+(size_t)lua_tointeger(L,lua_upvalueindex(3));
src<=ms.src_end;
src++){
const char*e;
ms.level=0;
if((e=match(&ms,src,p))!=NULL){
lua_Integer newstart=e-s;
if(e==src)newstart++;
lua_pushinteger(L,newstart);
lua_replace(L,lua_upvalueindex(3));
return push_captures(&ms,src,e);
}
}
return 0;
}
static int gmatch(lua_State*L){
luaL_checkstring(L,1);
luaL_checkstring(L,2);
lua_settop(L,2);
lua_pushinteger(L,0);
lua_pushcclosure(L,gmatch_aux,3);
return 1;
}
static void add_s(MatchState*ms,luaL_Buffer*b,const char*s,
const char*e){
size_t l,i;
const char*news=lua_tolstring(ms->L,3,&l);
for(i=0;i<l;i++){
if(news[i]!='%')
luaL_addchar(b,news[i]);
else{
i++;
if(!isdigit(uchar(news[i])))
luaL_addchar(b,news[i]);
else if(news[i]=='0')
luaL_addlstring(b,s,e-s);
else{
push_onecapture(ms,news[i]-'1',s,e);
luaL_addvalue(b);
}
}
}
}
static void add_value(MatchState*ms,luaL_Buffer*b,const char*s,
const char*e){
lua_State*L=ms->L;
switch(lua_type(L,3)){
case 3:
case 4:{
add_s(ms,b,s,e);
return;
}
case 6:{
int n;
lua_pushvalue(L,3);
n=push_captures(ms,s,e);
lua_call(L,n,1);
break;
}
case 5:{
push_onecapture(ms,0,s,e);
lua_gettable(L,3);
break;
}
}
if(!lua_toboolean(L,-1)){
lua_pop(L,1);
lua_pushlstring(L,s,e-s);
}
else if(!lua_isstring(L,-1))
luaL_error(L,"invalid replacement value (a %s)",luaL_typename(L,-1));
luaL_addvalue(b);
}
static int str_gsub(lua_State*L){
size_t srcl;
const char*src=luaL_checklstring(L,1,&srcl);
const char*p=luaL_checkstring(L,2);
int tr=lua_type(L,3);
int max_s=luaL_optint(L,4,srcl+1);
int anchor=(*p=='^')?(p++,1):0;
int n=0;
MatchState ms;
luaL_Buffer b;
luaL_argcheck(L,tr==3||tr==4||
tr==6||tr==5,3,
"string/function/table expected");
luaL_buffinit(L,&b);
ms.L=L;
ms.src_init=src;
ms.src_end=src+srcl;
while(n<max_s){
const char*e;
ms.level=0;
e=match(&ms,src,p);
if(e){
n++;
add_value(&ms,&b,src,e);
}
if(e&&e>src)
src=e;
else if(src<ms.src_end)
luaL_addchar(&b,*src++);
else break;
if(anchor)break;
}
luaL_addlstring(&b,src,ms.src_end-src);
luaL_pushresult(&b);
lua_pushinteger(L,n);
return 2;
}
static void addquoted(lua_State*L,luaL_Buffer*b,int arg){
size_t l;
const char*s=luaL_checklstring(L,arg,&l);
luaL_addchar(b,'"');
while(l--){
switch(*s){
case'"':case'\\':case'\n':{
luaL_addchar(b,'\\');
luaL_addchar(b,*s);
break;
}
case'\r':{
luaL_addlstring(b,"\\r",2);
break;
}
case'\0':{
luaL_addlstring(b,"\\000",4);
break;
}
default:{
luaL_addchar(b,*s);
break;
}
}
s++;
}
luaL_addchar(b,'"');
}
static const char*scanformat(lua_State*L,const char*strfrmt,char*form){
const char*p=strfrmt;
while(*p!='\0'&&strchr("-+ #0",*p)!=NULL)p++;
if((size_t)(p-strfrmt)>=sizeof("-+ #0"))
luaL_error(L,"invalid format (repeated flags)");
if(isdigit(uchar(*p)))p++;
if(isdigit(uchar(*p)))p++;
if(*p=='.'){
p++;
if(isdigit(uchar(*p)))p++;
if(isdigit(uchar(*p)))p++;
}
if(isdigit(uchar(*p)))
luaL_error(L,"invalid format (width or precision too long)");
*(form++)='%';
strncpy(form,strfrmt,p-strfrmt+1);
form+=p-strfrmt+1;
*form='\0';
return p;
}
static void addintlen(char*form){
size_t l=strlen(form);
char spec=form[l-1];
strcpy(form+l-1,"l");
form[l+sizeof("l")-2]=spec;
form[l+sizeof("l")-1]='\0';
}
static int str_format(lua_State*L){
int top=lua_gettop(L);
int arg=1;
size_t sfl;
const char*strfrmt=luaL_checklstring(L,arg,&sfl);
const char*strfrmt_end=strfrmt+sfl;
luaL_Buffer b;
luaL_buffinit(L,&b);
while(strfrmt<strfrmt_end){
if(*strfrmt!='%')
luaL_addchar(&b,*strfrmt++);
else if(*++strfrmt=='%')
luaL_addchar(&b,*strfrmt++);
else{
char form[(sizeof("-+ #0")+sizeof("l")+10)];
char buff[512];
if(++arg>top)
luaL_argerror(L,arg,"no value");
strfrmt=scanformat(L,strfrmt,form);
switch(*strfrmt++){
case'c':{
sprintf(buff,form,(int)luaL_checknumber(L,arg));
break;
}
case'd':case'i':{
addintlen(form);
sprintf(buff,form,(long)luaL_checknumber(L,arg));
break;
}
case'o':case'u':case'x':case'X':{
addintlen(form);
sprintf(buff,form,(unsigned long)luaL_checknumber(L,arg));
break;
}
case'e':case'E':case'f':
case'g':case'G':{
sprintf(buff,form,(double)luaL_checknumber(L,arg));
break;
}
case'q':{
addquoted(L,&b,arg);
continue;
}
case's':{
size_t l;
const char*s=luaL_checklstring(L,arg,&l);
if(!strchr(form,'.')&&l>=100){
lua_pushvalue(L,arg);
luaL_addvalue(&b);
continue;
}
else{
sprintf(buff,form,s);
break;
}
}
default:{
return luaL_error(L,"invalid option "LUA_QL("%%%c")" to "
LUA_QL("format"),*(strfrmt-1));
}
}
luaL_addlstring(&b,buff,strlen(buff));
}
}
luaL_pushresult(&b);
return 1;
}
static const luaL_Reg strlib[]={
{"byte",str_byte},
{"char",str_char},
{"find",str_find},
{"format",str_format},
{"gmatch",gmatch},
{"gsub",str_gsub},
{"lower",str_lower},
{"match",str_match},
{"rep",str_rep},
{"sub",str_sub},
{"upper",str_upper},
{NULL,NULL}
};
static void createmetatable(lua_State*L){
lua_createtable(L,0,1);
lua_pushliteral(L,"");
lua_pushvalue(L,-2);
lua_setmetatable(L,-2);
lua_pop(L,1);
lua_pushvalue(L,-2);
lua_setfield(L,-2,"__index");
lua_pop(L,1);
}
static int luaopen_string(lua_State*L){
luaL_register(L,"string",strlib);
createmetatable(L);
return 1;
}
static const luaL_Reg lualibs[]={
{"",luaopen_base},
{"table",luaopen_table},
{"io",luaopen_io},
{"os",luaopen_os},
{"string",luaopen_string},
{NULL,NULL}
};
static void luaL_openlibs(lua_State*L){
const luaL_Reg*lib=lualibs;
for(;lib->func;lib++){
lua_pushcfunction(L,lib->func);
lua_pushstring(L,lib->name);
lua_call(L,1,0);
}
}
typedef unsigned int UB;
static UB barg(lua_State*L,int idx){
union{lua_Number n;U64 b;}bn;
bn.n=lua_tonumber(L,idx)+6755399441055744.0;
if(bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number");
return(UB)bn.b;
}
#define BRET(b)lua_pushnumber(L,(lua_Number)(int)(b));return 1;
static int tobit(lua_State*L){
BRET(barg(L,1))}
static int bnot(lua_State*L){
BRET(~barg(L,1))}
static int band(lua_State*L){
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)}
static int bor(lua_State*L){
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)}
static int bxor(lua_State*L){
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)}
static int lshift(lua_State*L){
UB b=barg(L,1),n=barg(L,2)&31;BRET(b<<n)}
static int rshift(lua_State*L){
UB b=barg(L,1),n=barg(L,2)&31;BRET(b>>n)}
static int arshift(lua_State*L){
UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)}
static int rol(lua_State*L){
UB b=barg(L,1),n=barg(L,2)&31;BRET((b<<n)|(b>>(32-n)))}
static int ror(lua_State*L){
UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))}
static int bswap(lua_State*L){
UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)}
static int tohex(lua_State*L){
UB b=barg(L,1);
int n=lua_isnone(L,2)?8:(int)barg(L,2);
const char*hexdigits="0123456789abcdef";
char buf[8];
int i;
if(n<0){n=-n;hexdigits="0123456789ABCDEF";}
if(n>8)n=8;
for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;}
lua_pushlstring(L,buf,(size_t)n);
return 1;
}
static const struct luaL_Reg bitlib[]={
{"tobit",tobit},
{"bnot",bnot},
{"band",band},
{"bor",bor},
{"bxor",bxor},
{"lshift",lshift},
{"rshift",rshift},
{"arshift",arshift},
{"rol",rol},
{"ror",ror},
{"bswap",bswap},
{"tohex",tohex},
{NULL,NULL}
};
int main(int argc,char**argv){
lua_State*L=luaL_newstate();
int i;
luaL_openlibs(L);
luaL_register(L,"bit",bitlib);
if(argc<2)return sizeof(void*);
lua_createtable(L,0,1);
lua_pushstring(L,argv[1]);
lua_rawseti(L,-2,0);
lua_setglobal(L,"arg");
if(luaL_loadfile(L,argv[1]))
goto err;
for(i=2;i<argc;i++)
lua_pushstring(L,argv[i]);
if(lua_pcall(L,argc-2,0,0)){
err:
fprintf(stderr,"Error: %s\n",lua_tostring(L,-1));
return 1;
}
lua_close(L);
return 0;
}
project('fml', 'c',
version : '0.1',
default_options : ['warning_level=3'])
project(
'fml',
'c',
version: '0.1',
default_options: ['c_std=c11', 'warning_level=2'],
)
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required : false)
minilua = executable(
'minilua',
sources : files(
'dynasm/minilua.c',
),
dependencies : m_dep,
)
dynasm = generator(
minilua,
output : '@BASENAME@-dasm.c',
arguments : [meson.current_source_dir() + '/dynasm/dynasm.lua', '-o', '@OUTPUT@', '@INPUT@'],
)
 
exe = executable('fml',
'example_main.c',
'arena.c',
'parser.c',
install : true)
sources: files(
'reference/arena.c',
'reference/parser.c',
'src/main.c',
) + dynasm.process('src/jit.c'),
include_directories: include_directories(
'src',
'reference',
'dynasm',
),
install: true,
)
......@@ -35,8 +35,10 @@ arena_alloc(Arena *arena, size_t size)
new->prev = arena->current;
arena->current = new;
pos = align(sizeof(ArenaChunk), 8);
ASAN_POISON_MEMORY_REGION(((unsigned char *) new) + pos, new_size - pos);
}
arena->current->pos = pos + size;
ASAN_UNPOISON_MEMORY_REGION(((unsigned char *) arena->current) + pos, size);
return ((unsigned char *) arena->current) + pos;
}
 
......@@ -57,6 +59,7 @@ arena_restore(Arena *arena, size_t pos)
arena->prev_size_sum -= chunk->size;
}
chunk->pos = pos - arena->prev_size_sum;
ASAN_POISON_MEMORY_REGION(((unsigned char *) chunk) + chunk->pos, chunk->size - chunk->pos);
arena->current = chunk;
}
 
......
......@@ -128,6 +128,28 @@ size_t garena_cnt_from_(GArena *arena, size_t start, size_t elem_size);
} while (0)
 
 
// ASAN integeration
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
// https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning
void __asan_poison_memory_region(void const volatile *addr, size_t size);
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
#define ASAN_POISON_MEMORY_REGION(addr, size) \
__asan_poison_memory_region((addr), (size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
__asan_unpoison_memory_region((addr), (size))
#else
#define ASAN_POISON_MEMORY_REGION(addr, size) \
((void) (addr), (void) (size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
((void) (addr), (void) (size))
#endif
 
// PRIVATE IMPLEMENTATION DETAILS
 
......
......@@ -13,17 +13,31 @@ unreachable(char *file, size_t line)
exit(EXIT_FAILURE);
}
 
bool str_eq(Str a, Str b)
bool
str_eq(Str a, Str b)
{
return a.len == b.len && memcmp(a.str, b.str, a.len) == 0;
}
 
int str_cmp(Str a, Str b)
int
str_cmp(Str a, Str b)
{
int cmp = memcmp(a.str, b.str, a.len < b.len ? a.len : b.len);
return cmp == 0 ? (a.len > b.len) - (b.len > a.len) : cmp;
}
 
bool
time_get(struct timespec *ts)
{
#if defined(_WIN32) && defined(__MINGW32__)
return clock_gettime(CLOCK_REALTIME, ts) == 0;
#else
return timespec_get(ts, TIME_UTC) == TIME_UTC;
#endif
}
typedef struct {
const u8 *pos;
const u8 *end;
......@@ -176,7 +190,7 @@ lex_next(Lexer *lexer, Token *token)
u8 c = *lexer->pos;
switch (state) {
case LS_START: switch (c) {
case ' ': case '\t': case '\n': start += 1; break;
case ' ': case '\t': case '\n': case '\r': start += 1; break;
case ALPHA: state = LS_IDENTIFIER; break;
case DIGIT: state = LS_NUMBER; break;
case '"': state = LS_STRING; start += 1; break;
......@@ -222,6 +236,7 @@ lex_next(Lexer *lexer, Token *token)
default: tok = TK_SLASH; goto prev_done;
} break;
case LS_LINE_COMMENT: switch (c) {
// Also works with CR LF
case '\n': state = LS_START; start = lexer->pos + 1; break;
} break;
case LS_BLOCK_COMMENT: switch (c) {
......@@ -838,6 +853,7 @@ parser_error_cb(void *user_data, const u8 *err_pos, const char *msg, va_list ap)
size_t line = 0;
const u8 *pos = src->str;
for (; pos < err_pos; pos++) {
// Also works with CR LF
if (*pos == '\n') {
line_start = pos + 1;
line++;
......
......@@ -10,13 +10,14 @@
#include <stdbool.h>
#include <stdalign.h>
#include <string.h>
#include <time.h>
 
#include "arena.h"
 
// See the bottom of this file for parser API.
 
// This header mainly defines the AST emitted by the reference FML parser and
// a few utility types.
// a few utility types and functions.
 
typedef uint8_t u8;
typedef uint16_t u16;
......@@ -46,6 +47,25 @@ bool str_eq(Str a, Str b);
int str_cmp(Str a, Str b);
 
 
// Get current time and save it into `ts`. Platform details may differ, but
// `struct timespec` should be at least similar to the following:
//
// struct timespec {
// long long tv_sec; /* seconds */
// long tv_nsec; /* nanoseconds */
// }
//
// An example of use:
//
// struct timespec ts;
// if (!time_get(&ts)) {
// exit(1);
// }
// printf("%lld%09ld\n", ts.tv_sec, ts.tv_nsec);
//
bool time_get(struct timespec *ts);
// See https://courses.fit.cvut.cz/NI-RUN/specs/ast.html for details about the
// AST.
 
......
// for glibc < 2.37
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#if _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif
#if defined(__APPLE__) && defined(__MACH__)
#define MAP_JIT_VALUE MAP_JIT
void sys_icache_invalidate(void *start, size_t len);
#else
#define MAP_JIT_VALUE 0
#endif
#include "arena.h"
#include "jit.h" // dasm_proto.h
#include "dasm_x86.h"
//|.arch x64
//|.actionlist my_dasm_actions
//|.section code
//|.globals DASM_LBL_
// Avoid IDE warnings about names that will defined after DynASM preprocessing
// step.
//|| #define PREPROCESSED_WITH_DASM
#ifndef PREPROCESSED_WITH_DASM
unsigned char my_dasm_actions[] = {};
#define DASM_SECTION_CODE 0
#define DASM_MAXSECTION 1
enum {
DASM_LBL__MAX,
};
#endif
static void *
dasm_link_and_encode(Dst_DECL)
{
size_t size;
void* code;
assert(dasm_checkstep(Dst, 0) == DASM_S_OK);
assert(dasm_link(Dst, &size) == DASM_S_OK);
#ifdef _WIN32
code = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
code = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT_VALUE, -1, 0);
#endif
assert(dasm_encode(Dst, code) == DASM_S_OK);
#ifdef _WIN32
DWORD original;
VirtualProtect(code, size, PAGE_EXECUTE_READ, &original);
#else
mprotect(code, size, PROT_READ | PROT_EXEC);
#endif
#if defined(__APPLE__) && defined(__MACH__)
sys_icache_invalidate(code, size);
#endif
return code;
}
void
jit_init(Dst_DECL, Arena *arena)
{
(void) arena;
dasm_init(Dst, DASM_MAXSECTION);
ms->global_labels = arena_alloc(arena, DASM_LBL__MAX * sizeof(ms->global_labels[0]));
dasm_setupglobal(Dst, ms->global_labels, DASM_LBL__MAX);
}
void
jit_free(Dst_DECL)
{
dasm_free(Dst);
}
int_plus_const_func *
jit_addition_with_constant(Dst_DECL, int constant)
{
(void) constant;
dasm_setup(Dst, my_dasm_actions);
//| lea eax, [rdi + constant]
//| ret
return (int_plus_const_func *) dasm_link_and_encode(Dst);
}
#pragma once
#include "arena.h"
#include "mystate.h"
#define Dst ms
#define Dst_DECL MyState *ms
#define Dst_REF (ms->ds)
#define DASM_CHECKS
#include "dasm_proto.h"
typedef int (int_plus_const_func)(int);
void jit_init(Dst_DECL, Arena *arena);
void jit_free(Dst_DECL);
int_plus_const_func *jit_addition_with_constant(Dst_DECL, int constant);
#include <stdio.h>
#include <assert.h>
 
#include "arena.h"
#include "parser.h"
 
#include "jit.h"
int main(int argc, char **argv) {
#ifdef _WIN32
// Set standard output mode to "binary" on Windows.
// https://nullprogram.com/blog/2021/12/30/
int _setmode(int, int);
_setmode(1, 0x8000);
#endif
if (argc < 2) {
fprintf(stderr, "Error: expected at least one argument\n");
return 1;
......@@ -22,6 +32,14 @@ int main(int argc, char **argv) {
 
printf("Hello world!\n");
 
MyState my_state = {0};
jit_init(&my_state, &arena);
int_plus_const_func *add_5 = jit_addition_with_constant(&my_state, 5);
assert(add_5(3) == 8);
jit_free(&my_state);
arena_destroy(&arena);
 
return 0;
......
#pragma once
// Can't include "dasm_proto.h" here, since that would not see Dst anad Dst_DECL
// definitions from "jit.h" and use default ones. And "jit.h" can't be included
// here, since it requires "mystate.h".
typedef struct dasm_State dasm_State;
typedef struct {
dasm_State *ds;
void **global_labels;
} MyState;