summaryrefslogtreecommitdiff
path: root/src/lcode.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/lcode.js')
-rw-r--r--src/lcode.js223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/lcode.js b/src/lcode.js
new file mode 100644
index 0000000..695c7dc
--- /dev/null
+++ b/src/lcode.js
@@ -0,0 +1,223 @@
+/* jshint esversion: 6 */
+"use strict";
+
+const assert = require('assert');
+
+const lopcode = require('./lopcode.js');
+const llex = require('./llex.js');
+const lcode = require('./lcode.js');
+const OpCodesI = lopcode.OpCodesI;
+
+/*
+** 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;
+
+/*
+** 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, "control structure too long");
+ 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; /* nothing to concatenate? */
+ else if (l1 === NO_JUMP) /* no original list? */
+ l1[0] = l2;
+ else {
+ let list = l1[0];
+ let next = getjump(fs, list);
+ while (next !== NO_JUMP) { /* find last element */
+ list = next;
+ next = getjump(fs, list);
+ }
+ fixjump(fs, list, l2);
+ }
+};
+
+/*
+** 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);
+ luaK_concat(fs, j, jpc); /* keep them on hold */
+ return j;
+};
+
+/*
+** Code a 'return' instruction
+*/
+const luaK_ret = function(fs, first, nret) {
+ luaK_codeABC(fs, OpCodesI.OP_RETURN, first, nret + 1, 0);
+};
+
+/*
+** 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, 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;
+ }
+};
+
+/*
+** 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 */
+ 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 || i.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;
+ lcode.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) {
+ lua_assert(lopcode.getOpMode(o) == lopcode.iABx || getOpMode(o) == lopcode.iAsBx);
+ lua_assert(lopcode.getCMode(o) == lopcode.OpArgN);
+ lua_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);
+};
+
+module.exports.NO_JUMP = NO_JUMP;
+module.exports.luaK_codeABx = luaK_codeABx;
+module.exports.luaK_codeAsBx = luaK_codeAsBx;
+module.exports.luaK_getlabel = luaK_getlabel;
+module.exports.luaK_jump = luaK_jump;
+module.exports.luaK_patchclose = luaK_patchclose;
+module.exports.luaK_patchlist = luaK_patchlist;
+module.exports.luaK_patchtohere = luaK_patchtohere;
+module.exports.luaK_ret = luaK_ret; \ No newline at end of file