<commit_before>"use strict";

const assert   = require('assert');

const defs     = require('./defs.js');
const llex     = require('./llex.js');
const lobject  = require('./lobject.js');
const lopcode  = require('./lopcodes.js');
const lparser  = require('./lparser.js');
const lstring  = require('./lstring.js');
const ltable   = require('./ltable.js');
const ltm      = require('./ltm.js');
const lvm      = require('./lvm.js');

const CT       = defs.CT;
const OpCodesI = lopcode.OpCodesI;
const TValue   = lobject.TValue;

const luaO_arith = function(L, op, p1, p2, res) {
    switch (op) {
        case defs.LUA_OPBAND: case defs.LUA_OPBOR: case defs.LUA_OPBXOR:
        case defs.LUA_OPSHL: case defs.LUA_OPSHR:
        case defs.LUA_OPBNOT: {  /* operate only on integers */
            let i1 = lvm.tointeger(p1);
            let i2 = lvm.tointeger(p2);
            if (i1 !== false && i2 !== false) {
                res.type  = CT.LUA_TNUMINT;
                res.value = lobject.intarith(L, op, i1, i2);
                return;
            }
            else break;  /* go to the end */
        }
        case defs.LUA_OPDIV: case defs.LUA_OPPOW: {  /* operate only on floats */
            let n1 = lvm.tonumber(p1);
            let n2 = lvm.tonumber(p2);
            if (n1 !== false && n2 !== false) {
                res.type  = CT.LUA_TNUMFLT;
                res.value = lobject.numarith(L, op, n1, n2);
                return;
            }
            else break;  /* go to the end */
        }
        default: {  /* other operations */
            let n1 = lvm.tonumber(p1);
            let n2 = lvm.tonumber(p2);
            if (p1.ttisinteger() && p2.ttisinteger()) {
                res.type  = CT.LUA_TNUMINT;
                res.value = lobject.intarith(L, op, p1.value, p2.value);
                return;
            }
            else if (n1 !== false && n2 !== false) {
                res.type  = CT.LUA_TNUMFLT;
                res.value = lobject.numarith(L, op, n1, n2);
                return;
            }
            else break;  /* go to the end */
        }
    }
    /* could not perform raw operation; try metamethod */
    assert(L !== null);  /* should not fail when folding (compile time) */
    ltm.luaT_trybinTM(L, p1, p2, res, (op - defs.LUA_OPADD) + ltm.TMS.TM_ADD);
};

/* Maximum number of registers in a Lua function (must fit in 8 bits) */
const MAXREGS = 255;

/*
** Marks the end of a patch list. It is an invalid value both as an absolute
** address, and as a list link (would link an element to itself).
*/
const NO_JUMP = -1;

const BinOpr = {
    OPR_ADD:      0,
    OPR_SUB:      1,
    OPR_MUL:      2,
    OPR_MOD:      3,
    OPR_POW:      4,
    OPR_DIV:      5,
    OPR_IDIV:     6,
    OPR_BAND:     7,
    OPR_BOR:      8,
    OPR_BXOR:     9,
    OPR_SHL:      10,
    OPR_SHR:      11,
    OPR_CONCAT:   12,
    OPR_EQ:       13,
    OPR_LT:       14,
    OPR_LE:       15,
    OPR_NE:       16,
    OPR_GT:       17,
    OPR_GE:       18,
    OPR_AND:      19,
    OPR_OR:       20,
    OPR_NOBINOPR: 21
};

const UnOpr = {
    OPR_MINUS:    0,
    OPR_BNOT:     1,
    OPR_NOT:      2,
    OPR_LEN:      3,
    OPR_NOUNOPR:  4
};

const hasjumps = function(e) {
    return e.t !== e.f;
};

/*
** If expression is a numeric constant, fills 'v' with its value
** and returns true. Otherwise, returns false.
*/
const tonumeral = function(e, v) {
    let ek = lparser.expkind;
    if (hasjumps(e))
        return false;  /* not a numeral */
    switch (e.k) {
        case ek.VKINT:
            if (v) {
                v.type = CT.LUA_TNUMINT;
                v.value = e.u.ival;
            }
            return true;
        case ek.VKFLT:
            if (v) {
                v.type = CT.LUA_TNUMFLT;
                v.value = e.u.nval;
            }
            return true;
        default: return false;
    }
};

/*
** Create a OP_LOADNIL instruction, but try to optimize: if the previous
** instruction is also OP_LOADNIL and ranges are compatible, adjust
** range of previous instruction instead of emitting a new one. (For
** instance, 'local a; local b' will generate a single opcode.)
*/
const luaK_nil = function(fs, from, n) {
    let previous;
    let l = from + n - 1;  /* last register to set nil */
    if (fs.pc > fs.lasttarget) {  /* no jumps to current position? */
        previous = fs.f.code[fs.pc-1];
        if (previous.opcode === OpCodesI.OP_LOADNIL) {  /* previous is LOADNIL? */
            let pfrom = previous.A;  /* get previous range */
            let pl = pfrom + previous.B;
            if ((pfrom <= from && from <= pl + 1) ||
                    (from <= pfrom && pfrom <= l + 1)) {  /* can connect both? */
                if (pfrom < from) from = pfrom;  /* from = min(from, pfrom) */
                if (pl > l) l = pl;  /* l = max(l, pl) */
                lopcode.SETARG_A(previous, from);
                lopcode.SETARG_B(previous, l - from);
                return;
            }
        }  /* else go through */
    }
    luaK_codeABC(fs, OpCodesI.OP_LOADNIL, from, n - 1, 0);  /* else no optimization */
};

const getinstruction = function(fs, e) {
    return fs.f.code[e.u.info];
};

/*
** Gets the destination address of a jump instruction. Used to traverse
** a list of jumps.
*/
const getjump = function(fs, pc) {
    let offset = fs.f.code[pc].sBx;
    if (offset === NO_JUMP)  /* point to itself represents end of list */
        return NO_JUMP;  /* end of list */
    else
        return pc + 1 + offset;  /* turn offset into absolute position */
};

/*
** Fix jump instruction at position 'pc' to jump to 'dest'.
** (Jump addresses are relative in Lua)
*/
const fixjump = function(fs, pc, dest) {
    let jmp = fs.f.code[pc];
    let offset = dest - (pc + 1);
    assert(dest !== NO_JUMP);
    if (Math.abs(offset) > lopcode.MAXARG_sBx)
        llex.luaX_syntaxerror(fs.ls, defs.to_luastring("control structure too long", true));
    lopcode.SETARG_sBx(jmp, offset);
};

/*
** Concatenate jump-list 'l2' into jump-list 'l1'
*/
const luaK_concat = function(fs, l1, l2) {
    if (l2 === NO_JUMP) return l1;  /* nothing to concatenate? */
    else if (l1 === NO_JUMP)  /* no original list? */
        l1 = l2;
    else {
        let list = l1;
        let next = getjump(fs, list);
        while (next !== NO_JUMP) {  /* find last element */
            list = next;
            next = getjump(fs, list);
        }
        fixjump(fs, list, l2);
    }

    return l1;
};

/*
** Create a jump instruction and return its position, so its destination
** can be fixed later (with 'fixjump'). If there are jumps to
** this position (kept in 'jpc'), link them all together so that
** 'patchlistaux' will fix all them directly to the final destination.
*/
const luaK_jump = function (fs) {
    let jpc = fs.jpc;  /* save list of jumps to here */
    fs.jpc = NO_JUMP;  /* no more jumps to here */
    let j = luaK_codeAsBx(fs, OpCodesI.OP_JMP, 0, NO_JUMP);
    j = luaK_concat(fs, j, jpc);  /* keep them on hold */
    return j;
};

const luaK_jumpto = function(fs, t) {
    return luaK_patchlist(fs, luaK_jump(fs), t);
};

/*
** Code a 'return' instruction
*/
const luaK_ret = function(fs, first, nret) {
    luaK_codeABC(fs, OpCodesI.OP_RETURN, first, nret + 1, 0);
};

/*
** Code a "conditional jump", that is, a test or comparison opcode
** followed by a jump. Return jump position.
*/
const condjump = function(fs, op, A, B, C) {
   luaK_codeABC(fs, op, A, B, C);
   return luaK_jump(fs);
};

/*
** returns current 'pc' and marks it as a jump target (to avoid wrong
** optimizations with consecutive instructions not in the same basic block).
*/
const luaK_getlabel = function(fs) {
    fs.lasttarget = fs.pc;
    return fs.pc;
};

/*
** Returns the position of the instruction "controlling" a given
** jump (that is, its condition), or the jump itself if it is
** unconditional.
*/
const getjumpcontrol = function(fs, pc) {
    if (pc >= 1 && lopcode.testTMode(fs.f.code[pc - 1].opcode))
        return fs.f.code[pc - 1];
    else
        return fs.f.code[pc];
};

/*
** Patch destination register for a TESTSET instruction.
** If instruction in position 'node' is not a TESTSET, return 0 ("fails").
** Otherwise, if 'reg' is not 'NO_REG', set it as the destination
** register. Otherwise, change instruction to a simple 'TEST' (produces
** no register value)
*/
const patchtestreg = function(fs, node, reg) {
    let i = getjumpcontrol(fs, node);
    if (i.opcode !== OpCodesI.OP_TESTSET)
        return false;  /* cannot patch other instructions */
    if (reg !== lopcode.NO_REG && reg !== i.B)
        lopcode.SETARG_A(i, reg);
    else {
        /* no register to put value or register already has the value;
           change instruction to simple test */
        i = lopcode.CREATE_ABC(OpCodesI.OP_TEST, i.B, 0, i.C);
    }
    return true;
};

/*
** Traverse a list of tests ensuring no one produces a value
*/
const removevalues = function(fs, list) {
    for (; list !== NO_JUMP; list = getjump(fs, list))
        patchtestreg(fs, list, lopcode.NO_REG);
};

/*
** Traverse a list of tests, patching their destination address and
** registers: tests producing values jump to 'vtarget' (and put their
** values in 'reg'), other tests jump to 'dtarget'.
*/
const patchlistaux = function(fs, list, vtarget, reg, dtarget) {
    while (list !== NO_JUMP) {
        let next = getjump(fs, list);
        if (patchtestreg(fs, list, reg))
            fixjump(fs, list, vtarget);
        else
            fixjump(fs, list, dtarget);  /* jump to default target */
        list = next;
    }
};

/*
** Ensure all pending jumps to current position are fixed (jumping
** to current position with no values) and reset list of pending
** jumps
*/
const dischargejpc = function(fs) {
    patchlistaux(fs, fs.jpc, fs.pc, lopcode.NO_REG, fs.pc);
    fs.jpc = NO_JUMP;
};

/*
** Add elements in 'list' to list of pending jumps to "here"
** (current position)
*/
const luaK_patchtohere = function(fs, list) {
    luaK_getlabel(fs);  /* mark "here" as a jump target */
    fs.jpc = luaK_concat(fs, fs.jpc, list);
};

/*
** Path all jumps in 'list' to jump to 'target'.
** (The assert means that we cannot fix a jump to a forward address
** because we only know addresses once code is generated.)
*/
const luaK_patchlist = function(fs, list, target) {
    if (target === fs.pc)  /* 'target' is current position? */
        luaK_patchtohere(fs, list);  /* add list to pending jumps */
    else {
        assert(target < fs.pc);
        patchlistaux(fs, list, target, lopcode.NO_REG, target);
    }
};

/*
** Path all jumps in 'list' to close upvalues up to given 'level'
** (The assertion checks that jumps either were closing nothing
** or were closing higher levels, from inner blocks.)
*/
const luaK_patchclose = function(fs, list, level) {
    level++;  /* argument is +1 to reserve 0 as non-op */
    for (; list !== NO_JUMP; list = getjump(fs, list)) {
        let ins = fs.f.code[list];
        assert(ins.opcode === OpCodesI.OP_JMP && (ins.A === 0 || ins.A >= level));
        lopcode.SETARG_A(ins, level);
    }
};

/*
** Emit instruction 'i', checking for array sizes and saving also its
** line information. Return 'i' position.
*/
const luaK_code = function(fs, i) {
    let f = fs.f;
    dischargejpc(fs);  /* 'pc' will change */
    /* put new instruction in code array */
    f.code[fs.pc] = i;
    f.lineinfo[fs.pc] = fs.ls.lastline;
    return fs.pc++;
};

/*
** Format and emit an 'iABC' instruction. (Assertions check consistency
** of parameters versus opcode.)
*/
const luaK_codeABC = function(fs, o, a, b, c) {
    assert(lopcode.getOpMode(o) === lopcode.iABC);
    assert(lopcode.getBMode(o) !== lopcode.OpArgN || b === 0);
    assert(lopcode.getCMode(o) !== lopcode.OpArgN || c === 0);
    assert(a <= lopcode.MAXARG_A && b <= lopcode.MAXARG_B && c <= lopcode.MAXARG_C);
    return luaK_code(fs, lopcode.CREATE_ABC(o, a, b, c));
};

/*
** Format and emit an 'iABx' instruction.
*/
const luaK_codeABx = function(fs, o, a, bc) {
    assert(lopcode.getOpMode(o) === lopcode.iABx || lopcode.getOpMode(o) === lopcode.iAsBx);
    assert(lopcode.getCMode(o) === lopcode.OpArgN);
    assert(a <= lopcode.MAXARG_A && bc <= lopcode.MAXARG_Bx);
    return luaK_code(fs, lopcode.CREATE_ABx(o, a, bc));
};

const luaK_codeAsBx = function(fs,o,A,sBx) {
    return luaK_codeABx(fs, o, A, (sBx) + lopcode.MAXARG_sBx);
};

/*
** Emit an "extra argument" instruction (format 'iAx')
*/
const codeextraarg = function(fs, a) {
    assert(a <= lopcode.MAXARG_Ax);
    return luaK_code(fs, lopcode.CREATE_Ax(OpCodesI.OP_EXTRAARG, a));
};

/*
** Emit a "load constant" instruction, using either 'OP_LOADK'
** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX'
** instruction with "extra argument".
*/
const luaK_codek = function(fs, reg, k) {
    if (k <= lopcode.MAXARG_Bx)
        return luaK_codeABx(fs, OpCodesI.OP_LOADK, reg, k);
    else {
        let p = luaK_codeABx(fs, OpCodesI.OP_LOADKX, reg, 0);
        codeextraarg(fs, k);
        return p;
    }
};

/*
** Check register-stack level, keeping track of its maximum size
** in field 'maxstacksize'
*/
const luaK_checkstack = function(fs, n) {
    let newstack = fs.freereg + n;
    if (newstack > fs.f.maxstacksize) {
        if (newstack >= MAXREGS)
            llex.luaX_syntaxerror(fs.ls, defs.to_luastring("function or expression needs to many registers", true));
        fs.f.maxstacksize = newstack;
    }
};

/*
** Reserve 'n' registers in register stack
*/
const luaK_reserveregs = function(fs, n) {
   luaK_checkstack(fs, n);
   fs.freereg += n;
};

/*
** Free register 'reg', if it is neither a constant index nor
** a local variable.
*/
const freereg = function(fs, reg) {
    if (!lopcode.ISK(reg) && reg >= fs.nactvar) {
        fs.freereg--;
        assert(reg === fs.freereg);
    }
};

/*
** Free register used by expression 'e' (if any)
*/
const freeexp = function(fs, e) {
    if (e.k === lparser.expkind.VNONRELOC)
        freereg(fs, e.u.info);
};

/*
** Free registers used by expressions 'e1' and 'e2' (if any) in proper
** order.
*/
const freeexps = function(fs, e1, e2) {
    let r1 = (e1.k === lparser.expkind.VNONRELOC) ? e1.u.info : -1;
    let r2 = (e2.k === lparser.expkind.VNONRELOC) ? e2.u.info : -1;
    if (r1 > r2) {
        freereg(fs, r1);
        freereg(fs, r2);
    }
    else {
        freereg(fs, r2);
        freereg(fs, r1);
    }
};


/*
** Add constant 'v' to prototype's list of constants (field 'k').
** Use scanner's table to cache position of constants in constant list
** and try to reuse constants. Because some values should not be used
** as keys (nil cannot be a key, integer keys can collapse with float
** keys), the caller must provide a useful 'key' for indexing the cache.
*/
const addk = function(fs, key, v) {
    let f = fs.f;
    let idx = ltable.luaH_set(fs.ls.h, key);  /* index scanner table */
    if (idx && !idx.ttisnil()) {  /* is there an index there? */
        /* correct value? (warning: must distinguish floats from integers!) */
        if (idx.value < fs.nk && f.k[idx.value].ttype() === v.ttype() && f.k[idx.value].value === v.value)
            return idx.value;  /* reuse index */
    }
    /* constant not found; create a new entry */
    let k = fs.nk;
    idx.setivalue(k);<commit_msg>src/lcode.js: Fix luaH_set result check<commit_after>"use strict";

const assert   = require('assert');

const defs     = require('./defs.js');
const llex     = require('./llex.js');
const lobject  = require('./lobject.js');
const lopcode  = require('./lopcodes.js');
const lparser  = require('./lparser.js');
const lstring  = require('./lstring.js');
const ltable   = require('./ltable.js');
const ltm      = require('./ltm.js');
const lvm      = require('./lvm.js');

const CT       = defs.CT;
const OpCodesI = lopcode.OpCodesI;
const TValue   = lobject.TValue;

const luaO_arith = function(L, op, p1, p2, res) {
    switch (op) {
        case defs.LUA_OPBAND: case defs.LUA_OPBOR: case defs.LUA_OPBXOR:
        case defs.LUA_OPSHL: case defs.LUA_OPSHR:
        case defs.LUA_OPBNOT: {  /* operate only on integers */
            let i1 = lvm.tointeger(p1);
            let i2 = lvm.tointeger(p2);
            if (i1 !== false && i2 !== false) {
                res.type  = CT.LUA_TNUMINT;
                res.value = lobject.intarith(L, op, i1, i2);
                return;
            }
            else break;  /* go to the end */
        }
        case defs.LUA_OPDIV: case defs.LUA_OPPOW: {  /* operate only on floats */
            let n1 = lvm.tonumber(p1);
            let n2 = lvm.tonumber(p2);
            if (n1 !== false && n2 !== false) {
                res.type  = CT.LUA_TNUMFLT;
                res.value = lobject.numarith(L, op, n1, n2);
                return;
            }
            else break;  /* go to the end */
        }
        default: {  /* other operations */
            let n1 = lvm.tonumber(p1);
            let n2 = lvm.tonumber(p2);
            if (p1.ttisinteger() && p2.ttisinteger()) {
                res.type  = CT.LUA_TNUMINT;
                res.value = lobject.intarith(L, op, p1.value, p2.value);
                return;
            }
            else if (n1 !== false && n2 !== false) {
                res.type  = CT.LUA_TNUMFLT;
                res.value = lobject.numarith(L, op, n1, n2);
                return;
            }
            else break;  /* go to the end */
        }
    }
    /* could not perform raw operation; try metamethod */
    assert(L !== null);  /* should not fail when folding (compile time) */
    ltm.luaT_trybinTM(L, p1, p2, res, (op - defs.LUA_OPADD) + ltm.TMS.TM_ADD);
};

/* Maximum number of registers in a Lua function (must fit in 8 bits) */
const MAXREGS = 255;

/*
** Marks the end of a patch list. It is an invalid value both as an absolute
** address, and as a list link (would link an element to itself).
*/
const NO_JUMP = -1;

const BinOpr = {
    OPR_ADD:      0,
    OPR_SUB:      1,
    OPR_MUL:      2,
    OPR_MOD:      3,
    OPR_POW:      4,
    OPR_DIV:      5,
    OPR_IDIV:     6,
    OPR_BAND:     7,
    OPR_BOR:      8,
    OPR_BXOR:     9,
    OPR_SHL:      10,
    OPR_SHR:      11,
    OPR_CONCAT:   12,
    OPR_EQ:       13,
    OPR_LT:       14,
    OPR_LE:       15,
    OPR_NE:       16,
    OPR_GT:       17,
    OPR_GE:       18,
    OPR_AND:      19,
    OPR_OR:       20,
    OPR_NOBINOPR: 21
};

const UnOpr = {
    OPR_MINUS:    0,
    OPR_BNOT:     1,
    OPR_NOT:      2,
    OPR_LEN:      3,
    OPR_NOUNOPR:  4
};

const hasjumps = function(e) {
    return e.t !== e.f;
};

/*
** If expression is a numeric constant, fills 'v' with its value
** and returns true. Otherwise, returns false.
*/
const tonumeral = function(e, v) {
    let ek = lparser.expkind;
    if (hasjumps(e))
        return false;  /* not a numeral */
    switch (e.k) {
        case ek.VKINT:
            if (v) {
                v.type = CT.LUA_TNUMINT;
                v.value = e.u.ival;
            }
            return true;
        case ek.VKFLT:
            if (v) {
                v.type = CT.LUA_TNUMFLT;
                v.value = e.u.nval;
            }
            return true;
        default: return false;
    }
};

/*
** Create a OP_LOADNIL instruction, but try to optimize: if the previous
** instruction is also OP_LOADNIL and ranges are compatible, adjust
** range of previous instruction instead of emitting a new one. (For
** instance, 'local a; local b' will generate a single opcode.)
*/
const luaK_nil = function(fs, from, n) {
    let previous;
    let l = from + n - 1;  /* last register to set nil */
    if (fs.pc > fs.lasttarget) {  /* no jumps to current position? */
        previous = fs.f.code[fs.pc-1];
        if (previous.opcode === OpCodesI.OP_LOADNIL) {  /* previous is LOADNIL? */
            let pfrom = previous.A;  /* get previous range */
            let pl = pfrom + previous.B;
            if ((pfrom <= from && from <= pl + 1) ||
                    (from <= pfrom && pfrom <= l + 1)) {  /* can connect both? */
                if (pfrom < from) from = pfrom;  /* from = min(from, pfrom) */
                if (pl > l) l = pl;  /* l = max(l, pl) */
                lopcode.SETARG_A(previous, from);
                lopcode.SETARG_B(previous, l - from);
                return;
            }
        }  /* else go through */
    }
    luaK_codeABC(fs, OpCodesI.OP_LOADNIL, from, n - 1, 0);  /* else no optimization */
};

const getinstruction = function(fs, e) {
    return fs.f.code[e.u.info];
};

/*
** Gets the destination address of a jump instruction. Used to traverse
** a list of jumps.
*/
const getjump = function(fs, pc) {
    let offset = fs.f.code[pc].sBx;
    if (offset === NO_JUMP)  /* point to itself represents end of list */
        return NO_JUMP;  /* end of list */
    else
        return pc + 1 + offset;  /* turn offset into absolute position */
};

/*
** Fix jump instruction at position 'pc' to jump to 'dest'.
** (Jump addresses are relative in Lua)
*/
const fixjump = function(fs, pc, dest) {
    let jmp = fs.f.code[pc];
    let offset = dest - (pc + 1);
    assert(dest !== NO_JUMP);
    if (Math.abs(offset) > lopcode.MAXARG_sBx)
        llex.luaX_syntaxerror(fs.ls, defs.to_luastring("control structure too long", true));
    lopcode.SETARG_sBx(jmp, offset);
};

/*
** Concatenate jump-list 'l2' into jump-list 'l1'
*/
const luaK_concat = function(fs, l1, l2) {
    if (l2 === NO_JUMP) return l1;  /* nothing to concatenate? */
    else if (l1 === NO_JUMP)  /* no original list? */
        l1 = l2;
    else {
        let list = l1;
        let next = getjump(fs, list);
        while (next !== NO_JUMP) {  /* find last element */
            list = next;
            next = getjump(fs, list);
        }
        fixjump(fs, list, l2);
    }

    return l1;
};

/*
** Create a jump instruction and return its position, so its destination
** can be fixed later (with 'fixjump'). If there are jumps to
** this position (kept in 'jpc'), link them all together so that
** 'patchlistaux' will fix all them directly to the final destination.
*/
const luaK_jump = function (fs) {
    let jpc = fs.jpc;  /* save list of jumps to here */
    fs.jpc = NO_JUMP;  /* no more jumps to here */
    let j = luaK_codeAsBx(fs, OpCodesI.OP_JMP, 0, NO_JUMP);
    j = luaK_concat(fs, j, jpc);  /* keep them on hold */
    return j;
};

const luaK_jumpto = function(fs, t) {
    return luaK_patchlist(fs, luaK_jump(fs), t);
};

/*
** Code a 'return' instruction
*/
const luaK_ret = function(fs, first, nret) {
    luaK_codeABC(fs, OpCodesI.OP_RETURN, first, nret + 1, 0);
};

/*
** Code a "conditional jump", that is, a test or comparison opcode
** followed by a jump. Return jump position.
*/
const condjump = function(fs, op, A, B, C) {
   luaK_codeABC(fs, op, A, B, C);
   return luaK_jump(fs);
};

/*
** returns current 'pc' and marks it as a jump target (to avoid wrong
** optimizations with consecutive instructions not in the same basic block).
*/
const luaK_getlabel = function(fs) {
    fs.lasttarget = fs.pc;
    return fs.pc;
};

/*
** Returns the position of the instruction "controlling" a given
** jump (that is, its condition), or the jump itself if it is
** unconditional.
*/
const getjumpcontrol = function(fs, pc) {
    if (pc >= 1 && lopcode.testTMode(fs.f.code[pc - 1].opcode))
        return fs.f.code[pc - 1];
    else
        return fs.f.code[pc];
};

/*
** Patch destination register for a TESTSET instruction.
** If instruction in position 'node' is not a TESTSET, return 0 ("fails").
** Otherwise, if 'reg' is not 'NO_REG', set it as the destination
** register. Otherwise, change instruction to a simple 'TEST' (produces
** no register value)
*/
const patchtestreg = function(fs, node, reg) {
    let i = getjumpcontrol(fs, node);
    if (i.opcode !== OpCodesI.OP_TESTSET)
        return false;  /* cannot patch other instructions */
    if (reg !== lopcode.NO_REG && reg !== i.B)
        lopcode.SETARG_A(i, reg);
    else {
        /* no register to put value or register already has the value;
           change instruction to simple test */
        i = lopcode.CREATE_ABC(OpCodesI.OP_TEST, i.B, 0, i.C);
    }
    return true;
};

/*
** Traverse a list of tests ensuring no one produces a value
*/
const removevalues = function(fs, list) {
    for (; list !== NO_JUMP; list = getjump(fs, list))
        patchtestreg(fs, list, lopcode.NO_REG);
};

/*
** Traverse a list of tests, patching their destination address and
** registers: tests producing values jump to 'vtarget' (and put their
** values in 'reg'), other tests jump to 'dtarget'.
*/
const patchlistaux = function(fs, list, vtarget, reg, dtarget) {
    while (list !== NO_JUMP) {
        let next = getjump(fs, list);
        if (patchtestreg(fs, list, reg))
            fixjump(fs, list, vtarget);
        else
            fixjump(fs, list, dtarget);  /* jump to default target */
        list = next;
    }
};

/*
** Ensure all pending jumps to current position are fixed (jumping
** to current position with no values) and reset list of pending
** jumps
*/
const dischargejpc = function(fs) {
    patchlistaux(fs, fs.jpc, fs.pc, lopcode.NO_REG, fs.pc);
    fs.jpc = NO_JUMP;
};

/*
** Add elements in 'list' to list of pending jumps to "here"
** (current position)
*/
const luaK_patchtohere = function(fs, list) {
    luaK_getlabel(fs);  /* mark "here" as a jump target */
    fs.jpc = luaK_concat(fs, fs.jpc, list);
};

/*
** Path all jumps in 'list' to jump to 'target'.
** (The assert means that we cannot fix a jump to a forward address
** because we only know addresses once code is generated.)
*/
const luaK_patchlist = function(fs, list, target) {
    if (target === fs.pc)  /* 'target' is current position? */
        luaK_patchtohere(fs, list);  /* add list to pending jumps */
    else {
        assert(target < fs.pc);
        patchlistaux(fs, list, target, lopcode.NO_REG, target);
    }
};

/*
** Path all jumps in 'list' to close upvalues up to given 'level'
** (The assertion checks that jumps either were closing nothing
** or were closing higher levels, from inner blocks.)
*/
const luaK_patchclose = function(fs, list, level) {
    level++;  /* argument is +1 to reserve 0 as non-op */
    for (; list !== NO_JUMP; list = getjump(fs, list)) {
        let ins = fs.f.code[list];
        assert(ins.opcode === OpCodesI.OP_JMP && (ins.A === 0 || ins.A >= level));
        lopcode.SETARG_A(ins, level);
    }
};

/*
** Emit instruction 'i', checking for array sizes and saving also its
** line information. Return 'i' position.
*/
const luaK_code = function(fs, i) {
    let f = fs.f;
    dischargejpc(fs);  /* 'pc' will change */
    /* put new instruction in code array */
    f.code[fs.pc] = i;
    f.lineinfo[fs.pc] = fs.ls.lastline;
    return fs.pc++;
};

/*
** Format and emit an 'iABC' instruction. (Assertions check consistency
** of parameters versus opcode.)
*/
const luaK_codeABC = function(fs, o, a, b, c) {
    assert(lopcode.getOpMode(o) === lopcode.iABC);
    assert(lopcode.getBMode(o) !== lopcode.OpArgN || b === 0);
    assert(lopcode.getCMode(o) !== lopcode.OpArgN || c === 0);
    assert(a <= lopcode.MAXARG_A && b <= lopcode.MAXARG_B && c <= lopcode.MAXARG_C);
    return luaK_code(fs, lopcode.CREATE_ABC(o, a, b, c));
};

/*
** Format and emit an 'iABx' instruction.
*/
const luaK_codeABx = function(fs, o, a, bc) {
    assert(lopcode.getOpMode(o) === lopcode.iABx || lopcode.getOpMode(o) === lopcode.iAsBx);
    assert(lopcode.getCMode(o) === lopcode.OpArgN);
    assert(a <= lopcode.MAXARG_A && bc <= lopcode.MAXARG_Bx);
    return luaK_code(fs, lopcode.CREATE_ABx(o, a, bc));
};

const luaK_codeAsBx = function(fs,o,A,sBx) {
    return luaK_codeABx(fs, o, A, (sBx) + lopcode.MAXARG_sBx);
};

/*
** Emit an "extra argument" instruction (format 'iAx')
*/
const codeextraarg = function(fs, a) {
    assert(a <= lopcode.MAXARG_Ax);
    return luaK_code(fs, lopcode.CREATE_Ax(OpCodesI.OP_EXTRAARG, a));
};

/*
** Emit a "load constant" instruction, using either 'OP_LOADK'
** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX'
** instruction with "extra argument".
*/
const luaK_codek = function(fs, reg, k) {
    if (k <= lopcode.MAXARG_Bx)
        return luaK_codeABx(fs, OpCodesI.OP_LOADK, reg, k);
    else {
        let p = luaK_codeABx(fs, OpCodesI.OP_LOADKX, reg, 0);
        codeextraarg(fs, k);
        return p;
    }
};

/*
** Check register-stack level, keeping track of its maximum size
** in field 'maxstacksize'
*/
const luaK_checkstack = function(fs, n) {
    let newstack = fs.freereg + n;
    if (newstack > fs.f.maxstacksize) {
        if (newstack >= MAXREGS)
            llex.luaX_syntaxerror(fs.ls, defs.to_luastring("function or expression needs to many registers", true));
        fs.f.maxstacksize = newstack;
    }
};

/*
** Reserve 'n' registers in register stack
*/
const luaK_reserveregs = function(fs, n) {
   luaK_checkstack(fs, n);
   fs.freereg += n;
};

/*
** Free register 'reg', if it is neither a constant index nor
** a local variable.
*/
const freereg = function(fs, reg) {
    if (!lopcode.ISK(reg) && reg >= fs.nactvar) {
        fs.freereg--;
        assert(reg === fs.freereg);
    }
};

/*
** Free register used by expression 'e' (if any)
*/
const freeexp = function(fs, e) {
    if (e.k === lparser.expkind.VNONRELOC)
        freereg(fs, e.u.info);
};

/*
** Free registers used by expressions 'e1' and 'e2' (if any) in proper
** order.
*/
const freeexps = function(fs, e1, e2) {
    let r1 = (e1.k === lparser.expkind.VNONRELOC) ? e1.u.info : -1;
    let r2 = (e2.k === lparser.expkind.VNONRELOC) ? e2.u.info : -1;
    if (r1 > r2) {
        freereg(fs, r1);
        freereg(fs, r2);
    }
    else {
        freereg(fs, r2);
        freereg(fs, r1);
    }
};


/*
** Add constant 'v' to prototype's list of constants (field 'k').
** Use scanner's table to cache position of constants in constant list
** and try to reuse constants. Because some values should not be used
** as keys (nil cannot be a key, integer keys can collapse with float
** keys), the caller must provide a useful 'key' for indexing the cache.
*/
const addk = function(fs, key, v) {
    let f = fs.f;
    let idx = ltable.luaH_set(fs.ls.h, key);  /* index scanner table */
    if (idx.ttisinteger()) {  /* is there an index there? */
        let k = idx.value;
        /* correct value? (warning: must distinguish floats from integers!) */
        if (k < fs.nk && f.k[k].ttype() === v.ttype() && f.k[k].value === v.value)
            return k;  /* reuse index */
    }
    /* constant not found; create a new entry */
    let k = fs.nk;
    idx.setivalue(k);
{'fraction_non_alphanumeric': 0.1407446011530569, 'fraction_numerical': 0.008468779518582457, 'mean_word_length': 2.3686635944700463, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5343066', 'n_tokens_mistral': 10993, 'n_tokens_neox': 10709, 'n_words': 3237}