aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lapi.js13
-rw-r--r--src/lcode.js3
-rw-r--r--src/ldo.js7
-rw-r--r--src/llex.js9
-rw-r--r--src/lobject.js3
-rw-r--r--src/lparser.js24
-rw-r--r--src/lstate.js12
-rw-r--r--src/lstring.js33
-rw-r--r--src/ltable.js10
-rw-r--r--src/ltm.js3
-rw-r--r--src/lundump.js40
-rw-r--r--src/lvm.js5
12 files changed, 90 insertions, 72 deletions
diff --git a/src/lapi.js b/src/lapi.js
index aa8cf04..fa6da20 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -10,6 +10,7 @@ const lfunc = require('./lfunc.js');
const llex = require('./llex.js');
const lobject = require('./lobject.js');
const lstate = require('./lstate.js');
+const lstring = require('./lstring.js');
const ltm = require('./ltm.js');
const luaconf = require('./luaconf.js');
const lvm = require('./lvm.js');
@@ -216,7 +217,7 @@ const lua_pushlstring = function(L, s, len) {
assert(Array.isArray(s), "lua_pushlstring expects array of byte");
assert(typeof len === "number");
- let ts = len === 0 ? L.l_G.intern(defs.to_luastring("", true)) : new TValue(CT.LUA_TLNGSTR, s.slice(0, len));
+ let ts = new TValue(CT.LUA_TLNGSTR, lstring.luaS_bless(L, s.slice(0, len)));
L.stack[L.top++] = ts;
assert(L.top <= L.ci.top, "stack overflow");
@@ -230,7 +231,7 @@ const lua_pushstring = function (L, s) {
if (s === undefined || s === null)
L.stack[L.top] = new TValue(CT.LUA_TNIL, null);
else {
- L.stack[L.top] = new TValue(CT.LUA_TLNGSTR, s);
+ L.stack[L.top] = new TValue(CT.LUA_TLNGSTR, lstring.luaS_new(L, s));
}
L.top++;
@@ -255,7 +256,7 @@ const lua_pushliteral = function (L, s) {
if (s === undefined || s === null)
L.stack[L.top] = new TValue(CT.LUA_TNIL, null);
else {
- let ts = L.l_G.intern(defs.to_luastring(s));
+ let ts = new TValue(CT.LUA_TLNGSTR, lstring.luaS_newliteral(L, s));
L.stack[L.top] = ts;
}
@@ -330,7 +331,7 @@ const lua_pushglobaltable = function(L) {
const auxsetstr = function(L, t, k) {
assert(Array.isArray(k), "key must be an array of bytes");
- let str = L.l_G.intern(k);
+ let str = new TValue(CT.LUA_TLNGSTR, lstring.luaS_new(L, k));
assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack");
@@ -429,7 +430,7 @@ const lua_rawsetp = function(L, idx, p) {
const auxgetstr = function(L, t, k) {
assert(Array.isArray(k), "key must be an array of bytes");
- let str = L.l_G.intern(k);
+ let str = new TValue(CT.LUA_TLNGSTR, lstring.luaS_new(L, k));
L.stack[L.top++] = str;
assert(L.top <= L.ci.top, "stack overflow");
@@ -952,7 +953,7 @@ const lua_concat = function(L, n) {
if (n >= 2)
lvm.luaV_concat(L, n);
else if (n === 0) {
- L.stack[L.top++] = L.l_G.intern(defs.to_luastring("", true));
+ L.stack[L.top++] = new TValue(CT.LUA_TLNGSTR, lstring.luaS_newliteral(L, []));
assert(L.top <= L.ci.top, "stack overflow");
}
};
diff --git a/src/lcode.js b/src/lcode.js
index fac7d37..814498c 100644
--- a/src/lcode.js
+++ b/src/lcode.js
@@ -7,6 +7,7 @@ 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');
require('./lstate.js'); /* XXX: if this isn't here then things break on require("ltm") */
const ltm = require('./ltm.js');
@@ -514,7 +515,7 @@ const luaK_stringK = function(fs, s) {
*/
const luaK_intK = function(fs, n) {
/* FIXME: shouldn't use string as key. need to use pointer */
- let k = new TValue(CT.LUA_TLNGSTR, defs.to_luastring(`${n}`));
+ let k = new TValue(CT.LUA_TLNGSTR, lstring.luaS_bless(fs.L, defs.to_luastring(`${n}`)));
let o = new TValue(CT.LUA_TNUMINT, n);
return addk(fs, k, o);
};
diff --git a/src/ldo.js b/src/ldo.js
index 5865f96..39ba4dc 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -13,6 +13,7 @@ const llimit = require('./llimit.js');
const lobject = require('./lobject.js');
const lparser = require('./lparser.js');
const lstate = require('./lstate.js');
+const lstring = require('./lstring.js');
const ltm = require('./ltm.js');
const lvm = require('./lvm.js');
const CT = defs.constant_types;
@@ -23,11 +24,11 @@ const TValue = lobject.TValue;
const seterrorobj = function(L, errcode, oldtop) {
switch (errcode) {
case TS.LUA_ERRMEM: {
- L.stack[oldtop] = L.l_G.intern(defs.to_luastring("not enough memory", true));
+ L.stack[oldtop] = new TValue(CT.LUA_TLNGSTR, lstring.luaS_newliteral(L, "not enough memory"));
break;
}
case TS.LUA_ERRERR: {
- L.stack[oldtop] = L.l_G.intern(defs.to_luastring("error in error handling", true));
+ L.stack[oldtop] = new TValue(CT.LUA_TLNGSTR, lstring.luaS_newliteral(L, "error in error handling"));
break;
}
default: {
@@ -401,7 +402,7 @@ const recover = function(L, status) {
*/
const resume_error = function(L, msg, narg) {
L.top -= narg; /* remove args from the stack */
- L.stack[L.top++] = L.l_G.intern(defs.to_luastring(msg)); /* push error message */
+ L.stack[L.top++] = new TValue(CT.LUA_TLNGSTR, lstring.luaS_newliteral(L, msg)); /* push error message */
assert(L.top <= L.ci.top, "stack overflow");
return TS.LUA_ERRRUN;
};
diff --git a/src/llex.js b/src/llex.js
index 5591054..e690ea2 100644
--- a/src/llex.js
+++ b/src/llex.js
@@ -7,6 +7,7 @@ const ldebug = require('./ldebug.js');
const ldo = require('./ldo.js');
const ljstype = require('./ljstype');
const lobject = require('./lobject');
+const lstring = require('./lstring.js');
const llimit = require('./llimit.js');
const TS = defs.thread_status;
const char = defs.char;
@@ -231,7 +232,7 @@ const luaX_setinput = function(L, ls, z, source, firstchar) {
ls.linenumber = 1;
ls.lastline = 1;
ls.source = source;
- ls.envn = defs.to_luastring("_ENV", true);
+ ls.envn = lstring.luaS_newliteral(L, "_ENV");
};
const check_next1 = function(ls, c) {
@@ -368,7 +369,7 @@ const read_long_string = function(ls, seminfo, sep) {
}
if (seminfo)
- seminfo.ts = ls.buff.buffer.slice(2 + sep, 2 + sep - 2 * (2 + sep));
+ seminfo.ts = lstring.luaS_bless(ls.L, ls.buff.buffer.slice(2 + sep, 2 + sep - 2 * (2 + sep)));
};
const esccheck = function(ls, c, msg) {
@@ -493,7 +494,7 @@ const read_string = function(ls, del, seminfo) {
}
save_and_next(ls); /* skip delimiter */
- seminfo.ts = ls.buff.buffer.slice(1, ls.buff.n-1);
+ seminfo.ts = lstring.luaS_bless(ls.L, ls.buff.buffer.slice(1, ls.buff.n-1));
};
const isreserved = function(w) {
@@ -605,7 +606,7 @@ const llex = function(ls, seminfo) {
save_and_next(ls);
} while (ljstype.lislalnum(ls.current));
- let ts = ls.buff.buffer;
+ let ts = lstring.luaS_new(ls.L, ls.buff.buffer);
seminfo.ts = ts;
let kidx = luaX_tokens.slice(0, 22).indexOf(defs.to_jsstring(ts));
if (kidx >= 0) /* reserved word? */
diff --git a/src/lobject.js b/src/lobject.js
index cea9975..905eed1 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -6,6 +6,7 @@ const assert = require('assert');
const defs = require('./defs.js');
const ljstype = require('./ljstype.js');
const ldebug = require('./ldebug.js');
+const lstring = require('./lstring.js');
const luaconf = require('./luaconf.js');
const lvm = require('./lvm.js');
const llimit = require('./llimit.js');
@@ -418,7 +419,7 @@ const luaO_utf8esc = function(x) {
};
const pushstr = function(L, str) {
- L.stack[L.top++] = L.l_G.intern(str);
+ L.stack[L.top++] = new TValue(CT.LUA_TLNGSTR, lstring.luaS_new(L, str));
};
const luaO_pushvfstring = function(L, fmt, argp) {
diff --git a/src/lparser.js b/src/lparser.js
index 566576a..c90ff01 100644
--- a/src/lparser.js
+++ b/src/lparser.js
@@ -9,6 +9,7 @@ const llex = require('./llex.js');
const llimit = require('./llimit.js');
const lobject = require('./lobject.js');
const lopcode = require('./lopcodes.js');
+const lstring = require('./lstring.js');
const ltable = require('./ltable.js');
const BinOpr = lcode.BinOpr;
const OpCodesI = lopcode.OpCodesI;
@@ -24,6 +25,11 @@ const hasmultret = function(k) {
return k === expkind.VCALL || k === expkind.VVARARG;
};
+const eqstr = function(a, b) {
+ /* TODO: use plain equality as strings are cached */
+ return lstring.luaS_eqlngstr(a, b);
+};
+
class BlockCnt {
constructor() {
this.previous = null; /* chain */
@@ -274,7 +280,7 @@ const removevars = function(fs, tolevel) {
const searchupvalue = function(fs, name) {
let up = fs.f.upvalues;
for (let i = 0; i < fs.nups; i++) {
- if (up[i].name.join() === name.join())
+ if (eqstr(up[i].name, name))
return i;
}
return -1; /* not found */
@@ -293,7 +299,7 @@ const newupvalue = function(fs, name, v) {
const searchvar = function(fs, n) {
for (let i = fs.nactvar - 1; i >= 0; i--) {
- if (n.join() === getlocvar(fs, i).varname.join())
+ if (eqstr(n, getlocvar(fs, i).varname))
return i;
}
@@ -385,7 +391,7 @@ const closegoto = function(ls, g, label) {
let fs = ls.fs;
let gl = ls.dyd.gt;
let gt = gl.arr[g];
- assert(gt.name.join() === label.name.join());
+ assert(eqstr(gt.name, label.name));
if (gt.nactvar < label.nactvar) {
let vname = getlocvar(fs, gt.nactvar).varname;
let msg = lobject.luaO_pushfstring(ls.L, defs.to_luastring("<goto %s> at line %d jumps into the scope of local '%s'"),
@@ -409,7 +415,7 @@ const findlabel = function(ls, g) {
/* check labels in current block for a match */
for (let i = bl.firstlabel; i < dyd.label.n; i++) {
let lb = dyd.label.arr[i];
- if (lb.name.join() === gt.name.join()) { /* correct label? */
+ if (eqstr(lb.name, gt.name)) { /* correct label? */
if (gt.nactvar > lb.nactvar && (bl.upval || dyd.label.n > bl.firstlabel))
lcode.luaK_patchclose(ls.fs, gt.pc, lb.nactvar);
closegoto(ls, g, lb); /* close it */
@@ -438,7 +444,7 @@ const findgotos = function(ls, lb) {
let gl = ls.dyd.gt;
let i = ls.fs.bl.firstgoto;
while (i < gl.n) {
- if (gl.arr[i].name.join() === lb.name.join())
+ if (eqstr(gl.arr[i].name, lb.name))
closegoto(ls, i, lb);
else
i++;
@@ -483,7 +489,7 @@ const enterblock = function(fs, bl, isloop) {
** create a label named 'break' to resolve break statements
*/
const breaklabel = function(ls) {
- let n = defs.to_luastring("break", true);
+ let n = lstring.luaS_newliteral(ls.L, "break");
let l = newlabelentry(ls, ls.dyd.label, n, 0, ls.fs.pc);
findgotos(ls, ls.dyd.label.arr[l]);
};
@@ -1146,7 +1152,7 @@ const gotostat = function(ls, pc) {
label = str_checkname(ls);
else {
llex.luaX_next(ls); /* skip break */
- label = defs.to_luastring("break", true);
+ label = lstring.luaS_newliteral(ls.L, "break");
}
let g = newlabelentry(ls, ls.dyd.gt, label, line, pc);
findlabel(ls, g); /* close it if label already defined */
@@ -1155,7 +1161,7 @@ const gotostat = function(ls, pc) {
/* check for repeated labels on the same block */
const checkrepeated = function(fs, ll, label) {
for (let i = fs.bl.firstlabel; i < ll.n; i++) {
- if (label.join() === ll.arr[i].name.join()) {
+ if (eqstr(label, ll.arr[i].name)) {
semerror(fs.ls, defs.to_luastring(`label '${label.jsstring()}' already defined on line ${ll.arr[i].line}`));
}
}
@@ -1554,7 +1560,7 @@ const luaY_parser = function(L, z, buff, dyd, name, firstchar) {
lexstate.h = new TValue(defs.CT.LUA_TTABLE, ltable.luaH_new(L)); /* create table for scanner */
L.stack[L.top++] = lexstate.h;
funcstate.f = cl.p = new Proto(L);
- funcstate.f.source = name;
+ funcstate.f.source = lstring.luaS_new(L, name);
lexstate.buff = buff;
lexstate.dyd = dyd;
dyd.actvar.n = dyd.gt.n = dyd.label.n = 0;
diff --git a/src/lstate.js b/src/lstate.js
index fee308c..f2e17cd 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -71,18 +71,6 @@ class global_State {
this.mt = new Array(LUA_NUMTAGS);
}
- intern(stringArray) {
- // TODO: when weak value maps are available
- // let key = stringArray.map(e => `${e}|`).join('');
-
- // if (!this.strt.has(key))
- // this.strt.set(key, new lobject.TValue(CT.LUA_TLNGSTR, stringArray));
-
- // return this.strt.get(key);
-
- return new lobject.TValue(CT.LUA_TLNGSTR, stringArray);
- }
-
}
diff --git a/src/lstring.js b/src/lstring.js
new file mode 100644
index 0000000..34f988e
--- /dev/null
+++ b/src/lstring.js
@@ -0,0 +1,33 @@
+"use strict";
+
+const defs = require('./defs.js');
+
+const luaS_eqlngstr = function(a, b) {
+ return a == b || (a.length == b.length && a.join() == b.join());
+};
+
+/* converts strings (arrays) to a consistent map key */
+const luaS_hash = function(str) {
+ return str.map(e => `${e}|`).join('');
+};
+
+/* variant that takes ownership of array */
+const luaS_bless = function(L, str) {
+ return str;
+};
+
+/* makes a copy */
+const luaS_new = function(L, str) {
+ return luaS_bless(L, str.slice(0));
+};
+
+/* takes a js string */
+const luaS_newliteral = function(L, str) {
+ return luaS_bless(L, defs.to_luastring(str));
+};
+
+module.exports.luaS_eqlngstr = luaS_eqlngstr;
+module.exports.luaS_hash = luaS_hash;
+module.exports.luaS_bless = luaS_bless;
+module.exports.luaS_new = luaS_new;
+module.exports.luaS_newliteral = luaS_newliteral;
diff --git a/src/ltable.js b/src/ltable.js
index 428ee5e..de4c686 100644
--- a/src/ltable.js
+++ b/src/ltable.js
@@ -6,13 +6,9 @@ const assert = require('assert');
const defs = require('./defs.js');
const ldebug = require('./ldebug.js');
const lobject = require('./lobject.js');
+const lstring = require('./lstring.js');
const CT = defs.constant_types;
-/* converts strings (arrays) to a consistent map key */
-const hashstr = function(str) {
- return str.map(e => `${e}|`).join('');
-};
-
const table_hash = function(key) {
switch(key.type) {
case CT.LUA_TBOOLEAN:
@@ -28,7 +24,7 @@ const table_hash = function(key) {
return key.value;
case CT.LUA_TSHRSTR:
case CT.LUA_TLNGSTR:
- return hashstr(key.value);
+ return lstring.luaS_hash(key.value);
default:
throw new Error("unknown key type: " + key.type);
}
@@ -58,7 +54,7 @@ const luaH_getint = function(t, key) {
const luaH_getstr = function(t, key) {
assert(Array.isArray(key));
- return getgeneric(t, hashstr(key));
+ return getgeneric(t, lstring.luaS_hash(key));
};
const luaH_get = function(t, key) {
diff --git a/src/ltm.js b/src/ltm.js
index 4bf37fe..3647308 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -7,6 +7,7 @@ const defs = require('./defs.js');
const lobject = require('./lobject.js');
const ldo = require('./ldo.js');
const lstate = require('./lstate.js');
+const lstring = require('./lstring.js');
const ltable = require('./ltable.js');
const ldebug = require('./ldebug.js');
const lvm = require('./lvm.js');
@@ -61,7 +62,7 @@ const ttypename = function(t) {
const luaT_init = function(L) {
L.l_G.tmname = [];
for (let event in TMS)
- L.l_G.tmname.push(L.l_G.intern(TMS[event]));
+ L.l_G.tmname.push(new lobject.TValue(CT.LUA_TLNGSTR, lstring.luaS_new(L, TMS[event])));
};
/*
diff --git a/src/lundump.js b/src/lundump.js
index 1570588..3e56a84 100644
--- a/src/lundump.js
+++ b/src/lundump.js
@@ -7,6 +7,7 @@ const defs = require('./defs.js');
const lfunc = require('./lfunc.js');
const lobject = require('./lobject.js');
const lopcodes = require('./lopcodes.js');
+const lstring = require('./lstring.js');
const llex = require('./llex.js');
let LUAC_DATA = [0x19, 0x93, defs.char["\r"], defs.char["\n"], 0x1a, defs.char["\n"]];
@@ -36,6 +37,7 @@ class BytecodeParser {
read(size) {
let buffer = this.buffer.read(size);
+ assert(Array.isArray(buffer));
if (buffer.length < size) this.error("truncated");
return buffer;
}
@@ -74,8 +76,8 @@ class BytecodeParser {
return this.dv.getFloat64(0, true);
}
- read8bitString(n) {
- let size = typeof n !== 'undefined' ? n : Math.max(this.readByte() - 1, 0);
+ readString() {
+ let size = Math.max(this.readByte() - 1, 0);
if (size + 1 === 0xFF)
size = this.readSize_t() - 1;
@@ -84,25 +86,7 @@ class BytecodeParser {
return null;
}
- let string = [];
-
- for (let i = 0; i < size; i++)
- string.push(this.readByte());
-
- return string;
- }
-
- readString(n) {
- let size = typeof n !== 'undefined' ? n : Math.max(this.readByte() - 1, 0);
-
- if (size + 1 === 0xFF)
- size = this.readSize_t() - 1;
-
- if (size === 0) {
- return null;
- }
-
- return this.read(size);
+ return lstring.luaS_new(this.L, this.read(size));
}
/* creates a mask with 'n' 1 bits at position 'p' */
@@ -176,7 +160,7 @@ class BytecodeParser {
break;
case defs.CT.LUA_TSHRSTR:
case defs.CT.LUA_TLNGSTR:
- f.k.push(this.L.l_G.intern(this.read8bitString()));
+ f.k.push(new lobject.TValue(defs.CT.LUA_TLNGSTR, this.readString()));
break;
default:
this.error(`unrecognized constant '${t}'`);
@@ -229,9 +213,14 @@ class BytecodeParser {
this.readDebug(f);
}
+ checkliteral(s, msg) {
+ let buff = this.read(s.length);
+ if (buff.join() !== s.join())
+ this.error(msg);
+ }
+
checkHeader() {
- if (this.readString(3).join() !== defs.to_luastring(defs.LUA_SIGNATURE.substring(1)).join()) /* 1st char already checked */
- this.error("bad LUA_SIGNATURE, expected '<esc>Lua'");
+ this.checkliteral(defs.to_luastring(defs.LUA_SIGNATURE.substring(1)), "bad LUA_SIGNATURE, expected '<esc>Lua'"); /* 1st char already checked */
if (this.readByte() !== 0x53)
this.error("bad Lua version, expected 5.3");
@@ -239,8 +228,7 @@ class BytecodeParser {
if (this.readByte() !== 0)
this.error("supports only official PUC-Rio implementation");
- if (this.readString(6).join() !== LUAC_DATA.join())
- this.error("bytecode corrupted");
+ this.checkliteral(LUAC_DATA, "bytecode corrupted");
this.intSize = this.readByte();
this.size_tSize = this.readByte();
diff --git a/src/lvm.js b/src/lvm.js
index ef5d370..8bfef40 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -9,6 +9,7 @@ const luaconf = require('./luaconf.js');
const lobject = require('./lobject.js');
const lfunc = require('./lfunc.js');
const lstate = require('./lstate.js');
+const lstring = require('./lstring.js');
const llimit = require('./llimit.js');
const ldo = require('./ldo.js');
const ltm = require('./ltm.js');
@@ -991,7 +992,7 @@ const tostring = function(L, i) {
if (o.ttisstring()) return true;
if (o.ttisnumber() && !isNaN(o.value)) {
- L.stack[i] = L.l_G.intern(defs.to_luastring(`${o.value}`));
+ L.stack[i] = new lobject.TValue(CT.LUA_TLNGSTR, lstring.luaS_bless(L, defs.to_luastring(`${o.value}`)));
return true;
}
@@ -1029,7 +1030,7 @@ const luaV_concat = function(L, total) {
ts = ts.concat(L.stack[top - i].value);
}
- L.stack[top - n] = L.l_G.intern(ts);
+ L.stack[top - n] = new lobject.TValue(CT.LUA_TLNGSTR, lstring.luaS_bless(L, ts));
}
total -= n - 1; /* got 'n' strings to create 1 new */
L.top -= n - 1; /* popped 'n' strings and pushed one */