From 6be8db07196c407cd321a7b04f5022939c4ffce3 Mon Sep 17 00:00:00 2001
From: Benoit Giannangeli <giann008@gmail.com>
Date: Tue, 18 Apr 2017 11:38:21 +0200
Subject: Cache all to_luastring of internal literals

---
 src/lapi.js     |  4 ++--
 src/lauxlib.js  | 36 ++++++++++++++--------------
 src/lbaselib.js | 34 +++++++++++++-------------
 src/lcode.js    |  6 ++---
 src/lcorolib.js |  2 +-
 src/ldblib.js   | 48 ++++++++++++++++++-------------------
 src/ldebug.js   | 36 ++++++++++++++--------------
 src/ldo.js      | 12 +++++-----
 src/llex.js     | 26 ++++++++++----------
 src/lmathlib.js | 20 ++++++++--------
 src/lobject.js  |  2 +-
 src/lparser.js  | 44 +++++++++++++++++-----------------
 src/lstrlib.js  | 74 ++++++++++++++++++++++++++++-----------------------------
 src/ltablib.js  | 20 ++++++++--------
 src/ltm.js      | 54 ++++++++++++++++++++---------------------
 src/lua.js      | 11 ++++++++-
 src/lutf8lib.js | 20 ++++++++--------
 src/lvm.js      | 16 ++++++-------
 18 files changed, 237 insertions(+), 228 deletions(-)

(limited to 'src')

diff --git a/src/lapi.js b/src/lapi.js
index bf9b381..72c35f0 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -221,7 +221,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(lua.to_luastring("")) : new TValue(CT.LUA_TLNGSTR, s.slice(0, len));
+    let ts = len === 0 ? L.l_G.intern(lua.to_luastring("", true)) : new TValue(CT.LUA_TLNGSTR, s.slice(0, len));
     L.stack[L.top++] = ts;
 
     assert(L.top <= L.ci.top, "stack overflow");
@@ -938,7 +938,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(lua.to_luastring(""));
+        L.stack[L.top++] = L.l_G.intern(lua.to_luastring("", true));
         assert(L.top <= L.ci.top, "stack overflow");
     }
 };
diff --git a/src/lauxlib.js b/src/lauxlib.js
index 311e2eb..064e229 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -59,7 +59,7 @@ const findfield = function(L, objidx, level) {
 const pushglobalfuncname = function(L, ar) {
     let top = lapi.lua_gettop(L);
     ldebug.lua_getinfo(L, [char['f']], ar);  /* push function */
-    lapi.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(LUA_LOADED_TABLE));
+    lapi.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(LUA_LOADED_TABLE, true));
     if (findfield(L, top + 1, 2)) {
         let name = lapi.lua_tostring(L, -1);
         if (lobject.jsstring(name).startsWith("_G.")) {  /* name start with '_G.'? */
@@ -79,7 +79,7 @@ const sv = s => s ? s : [];
 
 const pushfuncname = function(L, ar) {
     if (pushglobalfuncname(L, ar)) {  /* try first a global name */
-        lapi.lua_pushstring(L, lua.to_luastring("function '").concat(lapi.lua_tostring(L, -1)).concat([char["'"]]));
+        lapi.lua_pushstring(L, lua.to_luastring("function '", true).concat(lapi.lua_tostring(L, -1)).concat([char["'"]]));
         lapi.lua_remove(L, -2);  /* remove name */
     }
     else if (ar.namewhat)  /* is there a name from code? */
@@ -87,7 +87,7 @@ const pushfuncname = function(L, ar) {
     else if (ar.what && ar.what[0] === char['m'])  /* main? */
         lapi.lua_pushliteral(L, "main chunk");
     else if (ar.what && ar.what[0] != char['C'])  /* for Lua functions, use <file:line> */
-        lapi.lua_pushstring(L, lua.to_luastring("function <").concat(...sv(ar.short_src), char[':'], ...lua.to_luastring(`${ar.linedefined}>`)));
+        lapi.lua_pushstring(L, lua.to_luastring("function <", true).concat(...sv(ar.short_src), char[':'], ...lua.to_luastring(`${ar.linedefined}>`)));
     else  /* nothing left... */
         lapi.lua_pushliteral(L, "?");
 };
@@ -121,7 +121,7 @@ const luaL_traceback = function(L, L1, msg, level) {
             lapi.lua_pushliteral(L, "\n\t...");  /* add a '...' */
             level = last - LEVELS2 + 1;  /* and skip to last ones */
         } else {
-            ldebug.lua_getinfo(L1, lua.to_luastring("Slnt"), ar);
+            ldebug.lua_getinfo(L1, lua.to_luastring("Slnt", true), ar);
             lapi.lua_pushstring(L, [char['\n'], char['\t'], char['.'], char['.'], char['.']].concat(ar.short_src));
             if (ar.currentline > 0)
                 lapi.lua_pushliteral(L, `${ar.currentline}:`);
@@ -147,7 +147,7 @@ const luaL_argerror = function(L, arg, extramsg) {
 
     ldebug.lua_getinfo(L, 'n', ar);
 
-    if (ar.namewhat === lua.to_luastring('method')) {
+    if (ar.namewhat === lua.to_luastring('method', true)) {
         arg--;  /* do not count 'self' */
         if (arg === 0)  /* error is in the self argument itself? */
             return luaL_error(L, lua.to_luastring(`calling '${lobject.jsstring(ar.name)}' on  bad self (${lobject.jsstring(extramsg)})`));
@@ -161,10 +161,10 @@ const luaL_argerror = function(L, arg, extramsg) {
 
 const typeerror = function(L, arg, tname) {
     let typearg;
-    if (luaL_getmetafield(L, arg, lua.to_luastring("__name")) === CT.LUA_TSTRING)
+    if (luaL_getmetafield(L, arg, lua.to_luastring("__name", true)) === CT.LUA_TSTRING)
         typearg = lapi.lua_tostring(L, -1);
     else if (lapi.lua_type(L, arg) === CT.LUA_TLIGHTUSERDATA)
-        typearg = lua.to_luastring("light userdata");
+        typearg = lua.to_luastring("light userdata", true);
     else
         typearg = luaL_typename(L, arg);
 
@@ -175,7 +175,7 @@ const typeerror = function(L, arg, tname) {
 const luaL_where = function(L, level) {
     let ar = new lua.lua_Debug();
     if (ldebug.lua_getstack(L, level, ar)) {
-        ldebug.lua_getinfo(L, lua.to_luastring("Sl"), ar);
+        ldebug.lua_getinfo(L, lua.to_luastring("Sl", true), ar);
         if (ar.currentline > 0) {
             lapi.lua_pushstring(L, lua.to_luastring(`${lobject.jsstring(ar.short_src)}:${ar.currentline}:`));
             return;
@@ -220,7 +220,7 @@ const luaL_argcheck = function(L, cond, arg, extramsg) {
 
 const luaL_checkany = function(L, arg) {
     if (lapi.lua_type(L, arg) === CT.LUA_TNONE)
-        luaL_argerror(L, arg, lua.to_luastring("value expected"));
+        luaL_argerror(L, arg, lua.to_luastring("value expected", true));
 };
 
 const luaL_checktype = function(L, arg, t) {
@@ -248,7 +248,7 @@ const luaL_optstring = luaL_optlstring;
 
 const interror = function(L, arg) {
     if (lapi.lua_isnumber(L, arg))
-        luaL_argerror(L, arg, lua.to_luastring("number has no integer representation"));
+        luaL_argerror(L, arg, lua.to_luastring("number has no integer representation", true));
     else
         tag_error(L, arg, CT.LUA_TNUMBER);
 };
@@ -357,15 +357,15 @@ const luaL_len = function(L, idx) {
     lapi.lua_len(L, idx);
     let l = lapi.lua_tointegerx(L, -1);
     if (l === false)
-        luaL_error(L, lua.to_luastring("object length is not an integer"));
+        luaL_error(L, lua.to_luastring("object length is not an integer", true));
     lapi.lua_pop(L, 1);  /* remove object */
     return l;
 };
 
 const luaL_tolstring = function(L, idx) {
-    if (luaL_callmeta(L, idx, lua.to_luastring("__tostring"))) {
+    if (luaL_callmeta(L, idx, lua.to_luastring("__tostring", true))) {
         if (!lapi.lua_isstring(L, -1))
-            luaL_error(L, lua.to_luastring("'__tostring' must return a string"));
+            luaL_error(L, lua.to_luastring("'__tostring' must return a string", true));
     } else {
         switch(lapi.lua_type(L, idx)) {
             case CT.LUA_TNUMBER:
@@ -379,7 +379,7 @@ const luaL_tolstring = function(L, idx) {
                 lapi.lua_pushstring(L, lua.to_luastring(`nil`));
                 break;
             default:
-                let tt = luaL_getmetafield(L, idx, lua.to_luastring("__name"));
+                let tt = luaL_getmetafield(L, idx, lua.to_luastring("__name", true));
                 let kind = tt === CT.LUA_TSTRING ? lapi.lua_tostring(L, -1) : luaL_typename(L, idx);
                 lapi.lua_pushstring(L, lua.to_luastring(`${lobject.jsstring(kind)}: 0x${lapi.index2addr(L, -1).id.toString(16)}`));
                 if (tt !== CT.LUA_TNIL)
@@ -438,7 +438,7 @@ const luaL_getsubtable = function(L, idx, fname) {
 ** Returns with only the table at the stack.
 */
 const luaL_setfuncs = function(L, l, nup) {
-    luaL_checkstack(L, nup, lua.to_luastring("too many upvalues"));
+    luaL_checkstack(L, nup, lua.to_luastring("too many upvalues", true));
     for (let lib in l) {  /* fill the table with given functions */
         for (let i = 0; i < nup; i++)  /* copy upvalues to the top */
             lapi.lua_pushvalue(L, -nup);
@@ -460,7 +460,7 @@ const luaL_checkstack = function(L, space, msg) {
         if (msg)
             luaL_error(L, lua.to_luastring(`stack overflow (${lobject.jsstring(msg)})`));
         else
-            luaL_error(L, lua.to_luastring('stack overflow'));
+            luaL_error(L, lua.to_luastring('stack overflow', true));
     }
 };
 
@@ -578,7 +578,7 @@ if (typeof require === "function") {
                 try {
                     lf.f = fs.openSync(jsfilename, "r");
                 } catch (e) {
-                    return errfile(L, lua.to_luastring("open"), fnameindex, e);
+                    return errfile(L, lua.to_luastring("open", true), fnameindex, e);
                 }
             }
 
@@ -597,7 +597,7 @@ if (typeof require === "function") {
                 return status;
             } catch (err) {
                 lapi.lua_settop(L, fnameindex);  /* ignore results from 'lua_load' */
-                return errfile(L, lua.to_luastring("read"), fnameindex);
+                return errfile(L, lua.to_luastring("read", true), fnameindex);
             }
         };
 
diff --git a/src/lbaselib.js b/src/lbaselib.js
index 931a285..af7c3cc 100644
--- a/src/lbaselib.js
+++ b/src/lbaselib.js
@@ -13,14 +13,14 @@ const luaB_print = function(L) {
     let n = lapi.lua_gettop(L); /* number of arguments */
     let str = [];
 
-    lapi.lua_getglobal(L, lua.to_luastring("tostring"));
+    lapi.lua_getglobal(L, lua.to_luastring("tostring", true));
     for (let i = 1; i <= n; i++) {
         lapi.lua_pushvalue(L, -1);  /* function to be called */
         lapi.lua_pushvalue(L, i);  /* value to print */
         lapi.lua_call(L, 1, 1);
         let s = lapi.lua_tolstring(L, -1);
         if (s === null)
-            return lauxlib.luaL_error(L, lua.to_luastring("'tostring' must return a string to 'print'"));
+            return lauxlib.luaL_error(L, lua.to_luastring("'tostring' must return a string to 'print'", true));
         if (i > 1) s = ["\t".charCodeAt(0)].concat(s);
         str = str.concat(s);
         lapi.lua_pop(L, 1);
@@ -45,16 +45,16 @@ const luaB_getmetatable = function(L) {
         lapi.lua_pushnil(L);
         return 1;  /* no metatable */
     }
-    lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable"));
+    lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable", true));
     return 1;  /* returns either __metatable field (if present) or metatable */
 };
 
 const luaB_setmetatable = function(L) {
     let t = lapi.lua_type(L, 2);
     lauxlib.luaL_checktype(L, 1, CT.LUA_TTABLE);
-    lauxlib.luaL_argcheck(L, t === CT.LUA_TNIL || t === CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected"));
-    if (lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable")) !== CT.LUA_TNIL)
-        return lauxlib.luaL_error(L, lua.to_luastring("cannot change a protected metatable"));
+    lauxlib.luaL_argcheck(L, t === CT.LUA_TNIL || t === CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected", true));
+    if (lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable", true)) !== CT.LUA_TNIL)
+        return lauxlib.luaL_error(L, lua.to_luastring("cannot change a protected metatable", true));
     lapi.lua_settop(L, 2);
     lapi.lua_setmetatable(L, 1);
     return 1;
@@ -69,7 +69,7 @@ const luaB_rawequal = function(L) {
 
 const luaB_rawlen = function(L) {
     let t = lapi.lua_type(L, 1);
-    lauxlib.luaL_argcheck(L, t === CT.LUA_TTABLE || t === CT.LUA_TSTRING, 1, lua.to_luastring("table or string expected"));
+    lauxlib.luaL_argcheck(L, t === CT.LUA_TTABLE || t === CT.LUA_TSTRING, 1, lua.to_luastring("table or string expected", true));
     lapi.lua_pushinteger(L, lapi.lua_rawlen(L, 1));
     return 1;
 };
@@ -93,7 +93,7 @@ const luaB_rawset = function(L) {
 
 const luaB_type = function(L) {
     let t = lapi.lua_type(L, 1);
-    lauxlib.luaL_argcheck(L, t !== CT.LUA_TNONE, 1, lua.to_luastring("value expected"));
+    lauxlib.luaL_argcheck(L, t !== CT.LUA_TNONE, 1, lua.to_luastring("value expected", true));
     lapi.lua_pushstring(L, lapi.lua_typename(L, t));
     return 1;
 };
@@ -124,7 +124,7 @@ const luaB_next = function(L) {
 };
 
 const luaB_pairs = function(L) {
-    return pairsmeta(L, lua.to_luastring("__pairs"), 0, luaB_next);
+    return pairsmeta(L, lua.to_luastring("__pairs", true), 0, luaB_next);
 };
 
 /*
@@ -166,7 +166,7 @@ const luaB_tonumber = function(L) {
         let base = lauxlib.luaL_checkinteger(L, 2);
         lauxlib.luaL_checktype(L, 1, CT.LUA_TSTRING);  /* no numbers as strings */
         let s = lapi.lua_tostring(L, 1);
-        lauxlib.luaL_argcheck(L, 2 <= base && base <= 36, 2, lua.to_luastring("base out of range"));
+        lauxlib.luaL_argcheck(L, 2 <= base && base <= 36, 2, lua.to_luastring("base out of range", true));
         let n = parseInt(lobject.jsstring(s), base);
         if (!isNaN(n)) {
             lapi.lua_pushinteger(L, n);
@@ -210,7 +210,7 @@ const luaB_select = function(L) {
         let i = lauxlib.luaL_checkinteger(L, 1);
         if (i < 0) i = n + i;
         else if (i > n) i = n;
-        lauxlib.luaL_argcheck(L, 1 <= i, 1, lua.to_luastring("index out of range"));
+        lauxlib.luaL_argcheck(L, 1 <= i, 1, lua.to_luastring("index out of range", true));
         return n - i;
     }
 };
@@ -284,28 +284,28 @@ const RESERVEDSLOT = 5;
 ** reserved slot inside the stack.
 */
 const generic_reader = function(L, ud) {
-    lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many nested functions"));
+    lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many nested functions", true));
     lapi.lua_pushvalue(L, 1);  /* get function */
     lapi.lua_call(L, 0, 1);  /* call it */
     if (lapi.lua_isnil(L, -1)) {
         lapi.lua_pop(L, 1);  /* pop result */
         return null;
     } else if (!lapi.lua_isstring(L, -1))
-        lauxlib.luaL_error(L, lua.to_luastring("reader function must return a string"));
+        lauxlib.luaL_error(L, lua.to_luastring("reader function must return a string", true));
     lapi.lua_replace(L, RESERVEDSLOT);  /* save string in reserved slot */
     return lapi.lua_tostring(L, RESERVEDSLOT);
 };
 
 const luaB_load = function(L) {
     let s = lapi.lua_tostring(L, 1);
-    let mode = lauxlib.luaL_optstring(L, 3, lua.to_luastring("bt"));
+    let mode = lauxlib.luaL_optstring(L, 3, lua.to_luastring("bt", true));
     let env = !lapi.lua_isnone(L, 4) ? 4 : 0;  /* 'env' index or 0 if no 'env' */
     let status;
     if (s !== null) {  /* loading a string? */
         let chunkname = lauxlib.luaL_optstring(L, 2, s);
         status = lauxlib.luaL_loadbufferx(L, s, chunkname, mode);
     } else {  /* loading from a reader function */
-        let chunkname = lauxlib.luaL_optstring(L, 2, lua.to_luastring("=(load)"));
+        let chunkname = lauxlib.luaL_optstring(L, 2, lua.to_luastring("=(load)", true));
         lauxlib.luaL_checktype(L, 1, CT.LUA_TFUNCTION);
         lapi.lua_settop(L, RESERVEDSLOT);  /* create reserved slot */
         status = lapi.lua_load(L, generic_reader, null, chunkname, mode);
@@ -378,10 +378,10 @@ const luaopen_base = function(L) {
     lauxlib.luaL_setfuncs(L, base_funcs, 0);
     /* set global _G */
     lapi.lua_pushvalue(L, -1);
-    lapi.lua_setfield(L, -2, lua.to_luastring("_G"));
+    lapi.lua_setfield(L, -2, lua.to_luastring("_G", true));
     /* set global _VERSION */
     lapi.lua_pushliteral(L, lua.LUA_VERSION);
-    lapi.lua_setfield(L, -2, lua.to_luastring("_VERSION"));
+    lapi.lua_setfield(L, -2, lua.to_luastring("_VERSION", true));
     return 1;
 };
 
diff --git a/src/lcode.js b/src/lcode.js
index 0b3d9bd..ec233c6 100644
--- a/src/lcode.js
+++ b/src/lcode.js
@@ -183,7 +183,7 @@ const fixjump = function(fs, pc, dest) {
     let offset = dest - (pc + 1);
     assert(dest !== NO_JUMP);
     if (Math.abs(offset) > lopcode.MAXARG_sBx)
-        llex.luaX_syntaxerror(fs.ls, lua.to_luastring("control structure too long"));
+        llex.luaX_syntaxerror(fs.ls, lua.to_luastring("control structure too long", true));
     lopcode.SETARG_sBx(jmp, offset);
 };
 
@@ -424,7 +424,7 @@ const luaK_checkstack = function(fs, n) {
     let newstack = fs.freereg + n;
     if (newstack > fs.f.maxstacksize) {
         if (newstack >= MAXREGS)
-            llex.luaX_syntaxerror(fs.ls, lua.to_luastring("function or expression needs to many registers"));
+            llex.luaX_syntaxerror(fs.ls, lua.to_luastring("function or expression needs to many registers", true));
         fs.f.maxstacksize = newstack;
     }
 };
@@ -1230,7 +1230,7 @@ const luaK_setlist = function(fs, base, nelems, tostore) {
         codeextraarg(fs, c);
     }
     else
-        llex.luaX_syntaxerror(fs.ls, lua.to_luastring("constructor too long"));
+        llex.luaX_syntaxerror(fs.ls, lua.to_luastring("constructor too long", true));
     fs.freereg = base + 1;  /* free registers with list values */
 };
 
diff --git a/src/lcorolib.js b/src/lcorolib.js
index 58d061c..b7c4555 100644
--- a/src/lcorolib.js
+++ b/src/lcorolib.js
@@ -14,7 +14,7 @@ const TS      = lua.thread_status;
 
 const getco = function(L) {
     let co = lapi.lua_tothread(L, 1);
-    lauxlib.luaL_argcheck(L, co, 1, lua.to_luastring("thread expected"));
+    lauxlib.luaL_argcheck(L, co, 1, lua.to_luastring("thread expected", true));
     return co;
 };
 
diff --git a/src/ldblib.js b/src/ldblib.js
index dfc7819..c637af2 100644
--- a/src/ldblib.js
+++ b/src/ldblib.js
@@ -33,7 +33,7 @@ const db_getmetatable = function(L) {
 
 const db_setmetatable = function(L) {
     const t = lapi.lua_type(L, 2);
-    lauxlib.luaL_argcheck(L, t == lua.CT.LUA_TNIL || t == lua.CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected"));
+    lauxlib.luaL_argcheck(L, t == lua.CT.LUA_TNIL || t == lua.CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected", true));
     lapi.lua_settop(L, 2);
     lapi.lua_setmetatable(L, 1);
     return 1;  /* return 1st argument */
@@ -123,7 +123,7 @@ const db_getinfo = function(L) {
     let thread = getthread(L);
     let arg = thread.arg;
     let L1 = thread.thread;
-    let options = lauxlib.luaL_optstring(L, arg + 2, lua.to_luastring("flnStu"));
+    let options = lauxlib.luaL_optstring(L, arg + 2, lua.to_luastring("flnStu", true));
     checkstack(L, L1, 3);
     if (lapi.lua_isfunction(L, arg + 1)) {  /* info about a function? */
         options = [char['>']].concat(options);  /* add '>' to 'options' */
@@ -137,31 +137,31 @@ const db_getinfo = function(L) {
     }
 
     if (!ldebug.lua_getinfo(L1, options, ar))
-        lauxlib.luaL_argerror(L, arg + 2, lua.to_luastring("invalid option"));
+        lauxlib.luaL_argerror(L, arg + 2, lua.to_luastring("invalid option", true));
     lapi.lua_newtable(L);  /* table to collect results */
     if (options.indexOf(char['S']) > -1) {
-        settabss(L, lua.to_luastring("source"), ar.source.value);
-        settabss(L, lua.to_luastring("short_src"), ar.short_src);
-        settabss(L, lua.to_luastring("linedefined"), lua.to_luastring(`${ar.linedefined}`));
-        settabss(L, lua.to_luastring("lastlinedefined"), lua.to_luastring(`${ar.lastlinedefined}`));
-        settabss(L, lua.to_luastring("what"), ar.what);
+        settabss(L, lua.to_luastring("source", true), ar.source.value);
+        settabss(L, lua.to_luastring("short_src", true), ar.short_src);
+        settabss(L, lua.to_luastring("linedefined", true), lua.to_luastring(`${ar.linedefined}`));
+        settabss(L, lua.to_luastring("lastlinedefined", true), lua.to_luastring(`${ar.lastlinedefined}`));
+        settabss(L, lua.to_luastring("what", true), ar.what);
     }
     if (options.indexOf(char['l']) > -1)
-        settabsi(L, lua.to_luastring("currentline"), ar.currentline);
+        settabsi(L, lua.to_luastring("currentline", true), ar.currentline);
     if (options.indexOf(char['u']) > -1)
-        settabsi(L, lua.to_luastring("nups"), ar.nups);
-        settabsi(L, lua.to_luastring("nparams"), ar.nparams);
-        settabsb(L, lua.to_luastring("isvararg"), ar.isvararg);
+        settabsi(L, lua.to_luastring("nups", true), ar.nups);
+        settabsi(L, lua.to_luastring("nparams", true), ar.nparams);
+        settabsb(L, lua.to_luastring("isvararg", true), ar.isvararg);
     if (options.indexOf(char['n']) > - 1) {
-        settabss(L, lua.to_luastring("name"), ar.name ? ar.name : null);
-        settabss(L, lua.to_luastring("namewhat"), ar.namewhat ? ar.namewhat : null);
+        settabss(L, lua.to_luastring("name", true), ar.name ? ar.name : null);
+        settabss(L, lua.to_luastring("namewhat", true), ar.namewhat ? ar.namewhat : null);
     }
     if (options.indexOf(char['t']) > - 1)
-        settabsb(L, lua.to_luastring("istailcall"), ar.istailcall);
+        settabsb(L, lua.to_luastring("istailcall", true), ar.istailcall);
     if (options.indexOf(char['L']) > - 1)
-        treatstackoption(L, L1, lua.to_luastring("activelines"));
+        treatstackoption(L, L1, lua.to_luastring("activelines", true));
     if (options.indexOf(char['f']) > - 1)
-        treatstackoption(L, L1, lua.to_luastring("func"));
+        treatstackoption(L, L1, lua.to_luastring("func", true));
     return 1;  /* return table */
 };
 
@@ -178,7 +178,7 @@ const db_getlocal = function(L) {
     } else {  /* stack-level argument */
         let level = lauxlib.luaL_checkinteger(L, arg + 1);
         if (!ldebug.lua_getstack(L1, level, ar))  /* out of range? */
-            return lauxlib.luaL_argerror(L, arg+1, lapi.to_luastring("level out of range"));
+            return lauxlib.luaL_argerror(L, arg+1, lapi.to_luastring("level out of range", true));
         checkstack(L, L1, 1);
         let name = ldebug.lua_getlocal(L1, ar, nvar);
         if (name) {
@@ -244,7 +244,7 @@ const db_setupvalue = function(L) {
 const checkupval = function(L, argf, argnup) {
     let nup = lauxlib.luaL_checkinteger(L, argnup);  /* upvalue index */
     lauxlib.luaL_checktype(L, argf, lua.CT.LUA_TFUNCTION);  /* closure */
-    lauxlib.luaL_argcheck(L, (lapi.lua_getupvalue(L, argf, nup) !== null), argnup, lua.to_luastring("invalid upvalue index"));
+    lauxlib.luaL_argcheck(L, (lapi.lua_getupvalue(L, argf, nup) !== null), argnup, lua.to_luastring("invalid upvalue index", true));
     return nup;
 };
 
@@ -257,8 +257,8 @@ const db_upvalueid = function(L) {
 const db_upvaluejoin = function(L) {
     let n1 = checkupval(L, 1, 2);
     let n2 = checkupval(L, 3, 4);
-    lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 1), 1, lua.to_luastring("Lua function expected"));
-    lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 3), 3, lua.to_luastring("Lua function expected"));
+    lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 1), 1, lua.to_luastring("Lua function expected", true));
+    lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 3), 3, lua.to_luastring("Lua function expected", true));
     lapi.lua_upvaluejoin(L, 1, n1, 3, n2);
     return 0;
 };
@@ -267,7 +267,7 @@ const db_upvaluejoin = function(L) {
 ** The hook table at registry[HOOKKEY] maps threads to their current
 ** hook function. (We only need the unique address of 'HOOKKEY'.)
 */
-const HOOKKEY = lua.to_luastring("__hooks__");
+const HOOKKEY = lua.to_luastring("__hooks__", true);
 
 const hooknames = ["call", "return", "line", "count", "tail call"].map(e => lua.to_luastring(e));
 
@@ -331,7 +331,7 @@ const db_sethook = function(L) {
         lapi.lua_pushvalue(L, -1);
         lapi.lua_rawsetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY);  /* set it in position */
         lapi.lua_pushstring(L, [char["k"]]);
-        lapi.lua_setfield(L, -2, lua.to_luastring("__mode"));  /** hooktable.__mode = "k" */
+        lapi.lua_setfield(L, -2, lua.to_luastring("__mode", true));  /** hooktable.__mode = "k" */
         lapi.lua_pushvalue(L, -1);
         lapi.lua_setmetatable(L, -2);  /* setmetatable(hooktable) = hooktable */
     }
@@ -423,7 +423,7 @@ if (typeof require === "function") {
                     continue;
 
                 let buffer = lua.to_luastring(input);
-                if (lauxlib.luaL_loadbuffer(L, buffer, buffer.length, lua.to_luastring("=(debug command)"))
+                if (lauxlib.luaL_loadbuffer(L, buffer, buffer.length, lua.to_luastring("=(debug command)", true))
                     || lapi.lua_pcall(L, 0, 0, 0)) {
                     lauxlib.lua_writestringerror(`${lapi.lua_tojsstring(L, -1)}\n`);
                 }
diff --git a/src/ldebug.js b/src/ldebug.js
index d769500..16449e2 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -94,7 +94,7 @@ const findvararg = function(ci, n, pos) {
     else {
         return {
             pos: ci.funcOff + nparams + n,
-            name: lua.to_luastring("(*vararg)")  /* generic name for any vararg */
+            name: lua.to_luastring("(*vararg)", true)  /* generic name for any vararg */
         };
     }
 };
@@ -115,7 +115,7 @@ const findlocal = function(L, ci, n) {
     if (name === null) {  /* no 'standard' name? */
         let limit = ci === L.ci ? L.top : ci.next.func;
         if (limit - base >= n && n > 0)  /* is 'n' inside 'ci' stack? */
-            name = lua.to_luastring("(*temporary)");  /* generic name for any valid slot */
+            name = lua.to_luastring("(*temporary)", true);  /* generic name for any valid slot */
         else
             return null;  /* no name */
     }
@@ -160,16 +160,16 @@ const lua_setlocal = function(L, ar, n) {
 
 const funcinfo = function(ar, cl) {
     if (cl === null || cl.type === CT.LUA_TCCL) {
-        ar.source = lua.to_luastring("=[JS]");
+        ar.source = lua.to_luastring("=[JS]", true);
         ar.linedefined = -1;
         ar.lastlinedefined = -1;
         ar.what = "J";
     } else {
         let p = cl.p;
-        ar.source = p.source ? p.source : lua.to_luastring("=?");
+        ar.source = p.source ? p.source : lua.to_luastring("=?", true);
         ar.linedefined = p.linedefined;
         ar.lastlinedefined = p.lastlinedefined;
-        ar.what = ar.linedefined === 0 ? lua.to_luastring("main") : lua.to_luastring("Lua");
+        ar.what = ar.linedefined === 0 ? lua.to_luastring("main", true) : lua.to_luastring("Lua", true);
     }
 
     ar.short_src = lobject.luaO_chunkid(ar.source, luaconf.LUA_IDSIZE);
@@ -198,8 +198,8 @@ const getfuncname = function(L, ci) {
     if (ci === null)
         return null;
     else if (ci.callstatus & lstate.CIST_FIN) {  /* is this a finalizer? */
-        r.name = lua.to_luastring("__gc");
-        r.funcname = lua.to_luastring("metamethod");  /* report it as such */
+        r.name = lua.to_luastring("__gc", true);
+        r.funcname = lua.to_luastring("metamethod", true);  /* report it as such */
         return r;
     }
     /* calling function is a known Lua function? */
@@ -373,7 +373,7 @@ const getobjname = function(p, lastpc, reg) {
     };
 
     if (r.name) {  /* is a local? */
-        r.funcname = lua.to_luastring("local");
+        r.funcname = lua.to_luastring("local", true);
         return r;
     }
 
@@ -396,12 +396,12 @@ const getobjname = function(p, lastpc, reg) {
                 let vn = op === 'OP_GETTABLE' ? lfunc.luaF_getlocalname(p, t + 1, pc) : upvalname(p, t);
                 vn = vn ? vn.jsstring() : null;
                 r.name = kname(p, pc, k).name;
-                r.funcname = vn && vn === "_ENV" ? lua.to_luastring("global") : lua.to_luastring("field");
+                r.funcname = vn && vn === "_ENV" ? lua.to_luastring("global", true) : lua.to_luastring("field", true);
                 return r;
             }
             case 'OP_GETUPVAL': {
                 r.name = upvalname(p, i.B);
-                r.funcname = lua.to_luastring("upvalue");
+                r.funcname = lua.to_luastring("upvalue", true);
                 return r;
             }
             case 'OP_LOADK':
@@ -409,7 +409,7 @@ const getobjname = function(p, lastpc, reg) {
                 let b = op === 'OP_LOADK' ? i.Bx : p.code[pc + 1].Ax;
                 if (p.k[b].ttisstring()) {
                     r.name = p.k[b].value;
-                    r.funcname = lua.to_luastring("constant");
+                    r.funcname = lua.to_luastring("constant", true);
                     return r;
                 }
                 break;
@@ -417,7 +417,7 @@ const getobjname = function(p, lastpc, reg) {
             case 'OP_SELF': {
                 let k = i.C;
                 r.name = kname(p, pc, k).name;
-                r.funcname = lua.to_luastring("method");
+                r.funcname = lua.to_luastring("method", true);
                 return r;
             }
             default: break;
@@ -446,7 +446,7 @@ const funcnamefromcode = function(L, ci) {
 
     if (ci.callstatus & lstate.CIST_HOOKED) {
         r.name = [lua.char["?"]];
-        r.funcname = lua.to_luastring("hook");
+        r.funcname = lua.to_luastring("hook", true);
         return r;
     }
 
@@ -455,8 +455,8 @@ const funcnamefromcode = function(L, ci) {
         case 'OP_TAILCALL':
             return getobjname(p, pc, i.A);  /* get function name */
         case 'OP_TFORCALL':
-            r.name = lua.to_luastring("for iterator");
-            r.funcname = lua.to_luastring("for iterator");
+            r.name = lua.to_luastring("for iterator", true);
+            r.funcname = lua.to_luastring("for iterator", true);
             return r;
         /* other instructions can do calls through metamethods */
         case 'OP_SELF':
@@ -492,7 +492,7 @@ const funcnamefromcode = function(L, ci) {
     }
 
     r.name = L.l_G.tmname[tm];
-    r.funcname = lua.to_luastring("metamethod");
+    r.funcname = lua.to_luastring("metamethod", true);
     return r;
 };
 
@@ -516,7 +516,7 @@ const getupvalname = function(L, ci, o, name) {
         if (c.upvals[i].val(L) === o) {
             return {
                 name: upvalname(c.p, i),
-                funcname: lua.to_luastring('upvalue')
+                funcname: lua.to_luastring('upvalue', true)
             };
         }
     }
@@ -544,7 +544,7 @@ const luaG_typeerror = function(L, o, op) {
 
 const luaG_concaterror = function(L, p1, p2) {
     if (p1.ttisstring() || p1.ttisnumber()) p1 = p2;
-    luaG_typeerror(L, p1, lua.to_luastring('concatenate'));
+    luaG_typeerror(L, p1, lua.to_luastring('concatenate', true));
 };
 
 /*
diff --git a/src/ldo.js b/src/ldo.js
index 0fcbf0e..a3521c8 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -23,11 +23,11 @@ const TValue         = lobject.TValue;
 const seterrorobj = function(L, errcode, oldtop) {
     switch (errcode) {
         case TS.LUA_ERRMEM: {
-            L.stack[oldtop] = L.l_G.intern(lua.to_luastring("not enough memory"));
+            L.stack[oldtop] = L.l_G.intern(lua.to_luastring("not enough memory", true));
             break;
         }
         case TS.LUA_ERRERR: {
-            L.stack[oldtop] = L.l_G.intern(lua.to_luastring("error in error handling"));
+            L.stack[oldtop] = L.l_G.intern(lua.to_luastring("error in error handling", true));
             break;
         }
         default: {
@@ -483,9 +483,9 @@ const lua_yieldk = function(L, nresults, ctx, k) {
 
     if (L.nny > 0) {
         if (L !== L.l_G.mainthread)
-            ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield across a JS-call boundary"));
+            ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield across a JS-call boundary", true));
         else
-            ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield from outside a coroutine"));
+            ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield from outside a coroutine", true));
     }
 
     L.status = TS.LUA_YIELD;
@@ -564,10 +564,10 @@ const f_parser = function(L, p) {
     let cl;
     let c = p.z.getc();  /* read first character */
     if (c === lua.LUA_SIGNATURE.charCodeAt(0)) {
-        checkmode(L, p.mode, lua.to_luastring("binary"));
+        checkmode(L, p.mode, lua.to_luastring("binary", true));
         cl = new BytecodeParser(L, p.z.buffer).luaU_undump();
     } else {
-        checkmode(L, p.mode, lua.to_luastring("text"));
+        checkmode(L, p.mode, lua.to_luastring("text", true));
         cl = lparser.luaY_parser(L, p.z, p.buff, p.dyd, p.name, c);
     }
 
diff --git a/src/llex.js b/src/llex.js
index 3ed0e56..b038619 100644
--- a/src/llex.js
+++ b/src/llex.js
@@ -152,7 +152,7 @@ const save = function(ls, c) {
     let b = ls.buff;
     if (b.n + 1 > b.buffer.length) {
         if (b.buffer.length >= Number.MAX_SAFE_INTEGER/2)
-            lexerror(ls, lua.to_luastring("lexical element too long"), 0);
+            lexerror(ls, lua.to_luastring("lexical element too long", true), 0);
     }
     b.buffer[b.n++] = c < 0 ? 255 + c + 1 : c;
 };
@@ -193,7 +193,7 @@ const inclinenumber = function(ls) {
     if (currIsNewline(ls) && ls.current !== old)
         next(ls);  /* skip '\n\r' or '\r\n' */
     if (++ls.linenumber >= Number.MAX_SAFE_INTEGER)
-        lexerror(ls, lua.to_luastring("chunk has too many lines"), 0);
+        lexerror(ls, lua.to_luastring("chunk has too many lines", true), 0);
 };
 
 const luaX_setinput = function(L, ls, z, source, firstchar) {
@@ -220,7 +220,7 @@ const luaX_setinput = function(L, ls, z, source, firstchar) {
     ls.linenumber = 1;
     ls.lastline = 1;
     ls.source = source;
-    ls.envn = L.l_G.intern(lua.to_luastring("_ENV"));
+    ls.envn = L.l_G.intern(lua.to_luastring("_ENV", true));
 };
 
 const check_next1 = function(ls, c) {
@@ -267,7 +267,7 @@ const read_numeral = function(ls, seminfo) {
 
     let obj = lobject.luaO_str2num(ls.buff.buffer);
     if (obj === false)  /* format error? */
-        lexerror(ls, lua.to_luastring("malformed number"), R.TK_FLT);
+        lexerror(ls, lua.to_luastring("malformed number", true), R.TK_FLT);
     if (obj.ttisinteger()) {
         seminfo.i = obj.value;
         return R.TK_INT;
@@ -373,7 +373,7 @@ const esccheck = function(ls, c, msg) {
 
 const gethexa = function(ls) {
     save_and_next(ls);
-    esccheck(ls, ljstype.lisxdigit(ls.current), lua.to_luastring("hexadecimal digit expected"));
+    esccheck(ls, ljstype.lisxdigit(ls.current), lua.to_luastring("hexadecimal digit expected", true));
     return lobject.luaO_hexavalue(ls.current);
 };
 
@@ -387,17 +387,17 @@ const readhexaesc = function(ls) {
 const readutf8desc = function(ls) {
     let i = 4;  /* chars to be removed: '\', 'u', '{', and first digit */
     save_and_next(ls);  /* skip 'u' */
-    esccheck(ls, ls.current === char['{'], lua.to_luastring("missing '{'"));
+    esccheck(ls, ls.current === char['{'], lua.to_luastring("missing '{'", true));
     let r = gethexa(ls);  /* must have at least one digit */
 
     save_and_next(ls);
     while (ljstype.lisxdigit(ls.current)) {
         i++;
         r = (r << 4) + lobject.luaO_hexavalue(ls.current);
-        esccheck(ls, r <= 0x10FFFF, lua.to_luastring("UTF-8 value too large"));
+        esccheck(ls, r <= 0x10FFFF, lua.to_luastring("UTF-8 value too large", true));
         save_and_next(ls);
     }
-    esccheck(ls, ls.current === char['}'], lua.to_luastring("missing '}'"));
+    esccheck(ls, ls.current === char['}'], lua.to_luastring("missing '}'", true));
     next(ls);  /* skip '}' */
     ls.buff.n -= i;  /* remove saved chars from buffer */
     return r;
@@ -417,7 +417,7 @@ const readdecesc = function(ls) {
         r = 10 * r + ls.current - char['0'];
         save_and_next(ls);
     }
-    esccheck(ls, r <= 255, lua.to_luastring("decimal escape too large"));
+    esccheck(ls, r <= 255, lua.to_luastring("decimal escape too large", true));
     ls.buff.n -= i;  /* remove read digits from buffer */
     return r;
 };
@@ -428,11 +428,11 @@ const read_string = function(ls, del, seminfo) {
     while (ls.current !== del) {
         switch (ls.current) {
             case -1:
-                lexerror(ls, lua.to_luastring("unfinished string"), R.TK_EOS);
+                lexerror(ls, lua.to_luastring("unfinished string", true), R.TK_EOS);
                 break;
             case char['\n']:
             case char['\r']:
-                lexerror(ls, lua.to_luastring("unfinished string"), R.TK_STRING);
+                lexerror(ls, lua.to_luastring("unfinished string", true), R.TK_STRING);
                 break;
             case char['\\']: {  /* escape sequences */
                 save_and_next(ls);  /* keep '\\' for error messages */
@@ -463,7 +463,7 @@ const read_string = function(ls, del, seminfo) {
                         will = 'no_save'; break;
                     }
                     default: {
-                        esccheck(ls, ljstype.lisdigit(ls.current), lua.to_luastring("invalid escape sequence"));
+                        esccheck(ls, ljstype.lisdigit(ls.current), lua.to_luastring("invalid escape sequence", true));
                         c = readdecesc(ls);  /* digital escape '\ddd' */
                         will = 'only_save'; break;
                     }
@@ -537,7 +537,7 @@ const llex = function(ls, seminfo) {
                     read_long_string(ls, seminfo, sep);
                     return R.TK_STRING;
                 } else if (sep !== -1)  /* '[=...' missing second bracket */
-                    lexerror(ls, lua.to_luastring("invalid long string delimiter"), R.TK_STRING);
+                    lexerror(ls, lua.to_luastring("invalid long string delimiter", true), R.TK_STRING);
                 return char['['];
             }
             case char['=']: {
diff --git a/src/lmathlib.js b/src/lmathlib.js
index 500331b..c052fcb 100644
--- a/src/lmathlib.js
+++ b/src/lmathlib.js
@@ -37,13 +37,13 @@ const math_random = function(L) {
             up = lauxlib.luaL_checkinteger(L, 2);
             break;
         }
-        default: return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments"));
+        default: return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments", true));
     }
 
     /* random integer in the interval [low, up] */
-    lauxlib.luaL_argcheck(L, low <= up, 1, lua.to_luastring("interval is empty"));
+    lauxlib.luaL_argcheck(L, low <= up, 1, lua.to_luastring("interval is empty", true));
     lauxlib.luaL_argcheck(L, low >= 0 || up <= Number.MAX_SAFE_INTEGER + low, 1,
-            lua.to_luastring("interval too large"));
+            lua.to_luastring("interval too large", true));
 
     r *= (up - low) + 1;
     lapi.lua_pushinteger(L, r + low);
@@ -173,7 +173,7 @@ const math_rad = function(L) {
 const math_min = function(L) {
     let n = lapi.lua_gettop(L);  /* number of arguments */
     let imin = 1;  /* index of current minimum value */
-    lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected"));
+    lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected", true));
     for (let i = 2; i <= n; i++){
         if (lapi.lua_compare(L, i, imin, lua.LUA_OPLT))
             imin = i;
@@ -185,7 +185,7 @@ const math_min = function(L) {
 const math_max = function(L) {
     let n = lapi.lua_gettop(L);  /* number of arguments */
     let imax = 1;  /* index of current minimum value */
-    lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected"));
+    lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected", true));
     for (let i = 2; i <= n; i++){
         if (lapi.lua_compare(L, imax, i, lua.LUA_OPLT))
             imax = i;
@@ -211,7 +211,7 @@ const math_fmod = function(L) {
     if (lapi.lua_isinteger(L, 1) && lapi.lua_isinteger(L, 2)) {
         let d = lapi.lua_tointeger(L, 2);
         if (Math.abs(d) + 1 <= 1) {
-            lauxlib.luaL_argcheck(L, d !== 0, 2, lua.to_luastring("zero"));
+            lauxlib.luaL_argcheck(L, d !== 0, 2, lua.to_luastring("zero", true));
             lapi.lua_pushinteger(L, 0);
         } else
             lapi.lua_pushinteger(L, lapi.lua_tointeger(L, 1) % d);
@@ -265,13 +265,13 @@ const mathlib = {
 const luaopen_math = function(L) {
     lauxlib.luaL_newlib(L, mathlib);
     lapi.lua_pushnumber(L, Math.PI);
-    lapi.lua_setfield(L, -2, lua.to_luastring("pi"));
+    lapi.lua_setfield(L, -2, lua.to_luastring("pi", true));
     lapi.lua_pushnumber(L, Number.MAX_VALUE);
-    lapi.lua_setfield(L, -2, lua.to_luastring("huge"));
+    lapi.lua_setfield(L, -2, lua.to_luastring("huge", true));
     lapi.lua_pushinteger(L, Number.MAX_SAFE_INTEGER);
-    lapi.lua_setfield(L, -2, lua.to_luastring("maxinteger"));
+    lapi.lua_setfield(L, -2, lua.to_luastring("maxinteger", true));
     lapi.lua_pushinteger(L, Number.MIN_SAFE_INTEGER);
-    lapi.lua_setfield(L, -2, lua.to_luastring("mininteger"));
+    lapi.lua_setfield(L, -2, lua.to_luastring("mininteger", true));
     return 1;
 };
 
diff --git a/src/lobject.js b/src/lobject.js
index 78434ae..52ca385 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -274,7 +274,7 @@ class LocVar {
     }
 }
 
-const RETS = lua.to_luastring("...");
+const RETS = lua.to_luastring("...", true);
 const PRE  = lua.to_luastring("[string \"");
 const POS  = lua.to_luastring("\"]");
 
diff --git a/src/lparser.js b/src/lparser.js
index bdaa160..cf5f55b 100644
--- a/src/lparser.js
+++ b/src/lparser.js
@@ -242,7 +242,7 @@ const new_localvar = function(ls, name) {
     let fs = ls.fs;
     let dyd = ls.dyd;
     let reg = registerlocalvar(ls, name);
-    checklimit(fs, dyd.actvar.n + 1 - fs.firstlocal, MAXVARS, lua.to_luastring("local variables"));
+    checklimit(fs, dyd.actvar.n + 1 - fs.firstlocal, MAXVARS, lua.to_luastring("local variables", true));
     dyd.actvar.arr[dyd.actvar.n] = new Vardesc();
     dyd.actvar.arr[dyd.actvar.n].idx = reg;
     dyd.actvar.n++;
@@ -282,7 +282,7 @@ const searchupvalue = function(fs, name) {
 
 const newupvalue = function(fs, name, v) {
     let f = fs.f;
-    checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, lua.to_luastring("upvalues"));
+    checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, lua.to_luastring("upvalues", true));
     f.upvalues[fs.nups] = new UpVal(fs.ls.L);
     f.upvalues[fs.nups].instack = v.k === expkind.VLOCAL;
     f.upvalues[fs.nups].idx = v.u.info;
@@ -373,7 +373,7 @@ const adjust_assign = function(ls, nvars, nexps, e) {
 const enterlevel = function(ls) {
     let L = ls.L;
     ++L.nCcalls;
-    checklimit(ls.fs, L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels"));
+    checklimit(ls.fs, L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels", true));
 };
 
 const leavelevel = function(ls) {
@@ -480,7 +480,7 @@ const enterblock = function(fs, bl, isloop) {
 ** create a label named 'break' to resolve break statements
 */
 const breaklabel = function(ls) {
-    let n = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break"));
+    let n = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break", true));
     let l = newlabelentry(ls, ls.dyd.label, n, 0, ls.fs.pc);
     findgotos(ls, ls.dyd.label.arr[l]);
 };
@@ -639,7 +639,7 @@ const recfield = function(ls, cc) {
     let val = new expdesc();
 
     if (ls.t.token === R.TK_NAME) {
-        checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor"));
+        checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor", true));
         checkname(ls, key);
     } else  /* ls->t.token === '[' */
         yindex(ls, key);
@@ -677,7 +677,7 @@ const lastlistfield = function(fs, cc) {
 const listfield = function(ls, cc) {
     /* listfield -> exp */
     expr(ls, cc.v);
-    checklimit(ls.fs, cc.na, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor"));
+    checklimit(ls.fs, cc.na, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor", true));
     cc.na++;
     cc.tostore++;
 };
@@ -749,7 +749,7 @@ const parlist = function(ls) {
                     f.is_vararg = 1;  /* declared vararg */
                     break;
                 }
-                default: llex.luaX_syntaxerror(ls, lua.to_luastring("<name> or '...' expected"));
+                default: llex.luaX_syntaxerror(ls, lua.to_luastring("<name> or '...' expected", true));
             }
         } while(!f.is_vararg && testnext(ls, char[',']));
     }
@@ -767,7 +767,7 @@ const body = function(ls, e, ismethod, line) {
     open_func(ls, new_fs, bl);
     checknext(ls, char['(']);
     if (ismethod) {
-        new_localvarliteral(ls, lua.to_luastring("self"));  /* create 'self' parameter */
+        new_localvarliteral(ls, lua.to_luastring("self", true));  /* create 'self' parameter */
         adjustlocalvars(ls, 1);
     }
     parlist(ls);
@@ -816,7 +816,7 @@ const funcargs = function(ls, f, line) {
             break;
         }
         default: {
-            llex.luaX_syntaxerror(ls, lua.to_luastring("function arguments expected"));
+            llex.luaX_syntaxerror(ls, lua.to_luastring("function arguments expected", true));
         }
     }
     assert(f.k === expkind.VNONRELOC);
@@ -856,7 +856,7 @@ const primaryexp = function(ls, v) {
             return;
         }
         default: {
-            llex.luaX_syntaxerror(ls, lua.to_luastring("unexpected symbol"));
+            llex.luaX_syntaxerror(ls, lua.to_luastring("unexpected symbol", true));
         }
     }
 };
@@ -930,7 +930,7 @@ const simpleexp = function(ls, v) {
         }
         case R.TK_DOTS: {  /* vararg */
             let fs = ls.fs;
-            check_condition(ls, fs.f.is_vararg, lua.to_luastring("cannot use '...' outside a vararg function"));
+            check_condition(ls, fs.f.is_vararg, lua.to_luastring("cannot use '...' outside a vararg function", true));
             init_exp(v, expkind.VVARARG, lcode.luaK_codeABC(fs, OpCodesI.OP_VARARG, 0, 1, 0));
             break;
         }
@@ -1102,14 +1102,14 @@ const check_conflict = function(ls, lh, v) {
 
 const assignment = function(ls, lh, nvars) {
     let e = new expdesc();
-    check_condition(ls, vkisvar(lh.v.k), lua.to_luastring("syntax error"));
+    check_condition(ls, vkisvar(lh.v.k), lua.to_luastring("syntax error", true));
     if (testnext(ls, char[','])) {  /* assignment -> ',' suffixedexp assignment */
         let nv = new LHS_assign();
         nv.prev = lh;
         suffixedexp(ls, nv.v);
         if (nv.v.k !== expkind.VINDEXED)
             check_conflict(ls, lh, nv.v);
-        checklimit(ls.fs, nvars + ls.L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels"));
+        checklimit(ls.fs, nvars + ls.L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels", true));
         assignment(ls, nv, nvars + 1);
     } else {  /* assignment -> '=' explist */
         checknext(ls, char['=']);
@@ -1142,7 +1142,7 @@ const gotostat = function(ls, pc) {
         label = str_checkname(ls);
     else {
         llex.luaX_next(ls);  /* skip break */
-        label = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break"));
+        label = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break", true));
     }
     let g = newlabelentry(ls, ls.dyd.gt, label, line, pc);
     findlabel(ls, g);  /* close it if label already defined */
@@ -1253,9 +1253,9 @@ const fornum = function(ls, varname, line) {
     /* fornum -> NAME = exp1,exp1[,exp1] forbody */
     let fs = ls.fs;
     let base = fs.freereg;
-    new_localvarliteral(ls, lua.to_luastring("(for index)"));
-    new_localvarliteral(ls, lua.to_luastring("(for limit)"));
-    new_localvarliteral(ls, lua.to_luastring("(for step)"));
+    new_localvarliteral(ls, lua.to_luastring("(for index)", true));
+    new_localvarliteral(ls, lua.to_luastring("(for limit)", true));
+    new_localvarliteral(ls, lua.to_luastring("(for step)", true));
     new_localvar(ls, varname);
     checknext(ls, char['=']);
     exp1(ls);  /* initial value */
@@ -1277,9 +1277,9 @@ const forlist = function(ls, indexname) {
     let nvars = 4;  /* gen, state, control, plus at least one declared var */
     let base = fs.freereg;
     /* create control variables */
-    new_localvarliteral(ls, lua.to_luastring("(for generator)"));
-    new_localvarliteral(ls, lua.to_luastring("(for state)"));
-    new_localvarliteral(ls, lua.to_luastring("(for control)"));
+    new_localvarliteral(ls, lua.to_luastring("(for generator)", true));
+    new_localvarliteral(ls, lua.to_luastring("(for state)", true));
+    new_localvarliteral(ls, lua.to_luastring("(for control)", true));
     /* create declared variables */
     new_localvar(ls, indexname);
     while (testnext(ls, char[','])) {
@@ -1303,7 +1303,7 @@ const forstat = function(ls, line) {
     switch (ls.t.token) {
         case char['=']: fornum(ls, varname, line); break;
         case char[',']: case R.TK_IN: forlist(ls, varname); break;
-        default: llex.luaX_syntaxerror(ls, lua.to_luastring("'=' or 'in' expected"));
+        default: llex.luaX_syntaxerror(ls, lua.to_luastring("'=' or 'in' expected", true));
     }
     check_match(ls, R.TK_END, R.TK_FOR, line);
     leaveblock(fs);  /* loop scope ('break' jumps to this point) */
@@ -1421,7 +1421,7 @@ const exprstat= function(ls) {
         assignment(ls, v, 1);
     }
     else {  /* stat -> func */
-        check_condition(ls, v.v.k === expkind.VCALL, lua.to_luastring("syntax error"));
+        check_condition(ls, v.v.k === expkind.VCALL, lua.to_luastring("syntax error", true));
         lopcode.SETARG_C(lcode.getinstruction(fs, v.v), 1);  /* call statement uses no results */
     }
 };
diff --git a/src/lstrlib.js b/src/lstrlib.js
index 73fc068..c2ee817 100644
--- a/src/lstrlib.js
+++ b/src/lstrlib.js
@@ -103,11 +103,11 @@ const num2straux = function(x) {
     let buff = [];
     /* if 'inf' or 'NaN', format it like '%g' */
     if (Object.is(x, Infinity))
-        return lua.to_luastring('inf');
+        return lua.to_luastring('inf', true);
     else if (Object.is(x, -Infinity))
-        return lua.to_luastring('-inf');
+        return lua.to_luastring('-inf', true);
     else if (Number.isNaN(x))
-        return lua.to_luastring('nan');
+        return lua.to_luastring('nan', true);
     else if (x === 0) {  /* can be -0... */
         /* create "0" or "-0" followed by exponent */
         let zero = sprintf(luaconf.LUA_NUMBER_FMT + "x0p+0", x).split('').map(e => e.charCodeAt(0));
@@ -144,7 +144,7 @@ const lua_number2strx = function(L, fmt, x) {
         for (let i = 0; i < buff.length; i++)
             buff[i] = char[String.fromCharCode(buff[i]).toUpperCase()];
     } else if (fmt[SIZELENMOD] !== char['a'])
-        lauxlib.luaL_error(L, lua.to_luastring("modifiers for format '%a'/'%A' not implemented"));
+        lauxlib.luaL_error(L, lua.to_luastring("modifiers for format '%a'/'%A' not implemented", true));
     return buff;
 };
 
@@ -239,7 +239,7 @@ const addliteral = function(L, b, arg) {
             break;
         }
         default: {
-            lauxlib.luaL_argerror(L, arg, lua.to_luastring("value has no literal form"));
+            lauxlib.luaL_argerror(L, arg, lua.to_luastring("value has no literal form", true));
         }
     }
 };
@@ -248,7 +248,7 @@ const scanformat = function(L, strfrmt, form) {
     let p = strfrmt;
     while (p[0] !== 0 && FLAGS.indexOf(p[0]) >= 0) p = p.slice(1);  /* skip flags */
     if (strfrmt.length - p.length >= FLAGS.length)
-        lauxlib.luaL_error(L, lua.to_luastring("invalid format (repeated flags)"));
+        lauxlib.luaL_error(L, lua.to_luastring("invalid format (repeated flags)", true));
     if (isdigit(p[0])) p = p.slice(1);  /* skip width */
     if (isdigit(p[0])) p = p.slice(1);  /* (2 digits at most) */
     if (p[0] === char['.']) {
@@ -257,7 +257,7 @@ const scanformat = function(L, strfrmt, form) {
         if (isdigit(p[0])) p = p.slice(1);  /* (2 digits at most) */
     }
     if (isdigit(p[0]))
-        lauxlib.luaL_error(L, lua.to_luastring("invalid format (width or precision too long)"));
+        lauxlib.luaL_error(L, lua.to_luastring("invalid format (width or precision too long)", true));
     form[0] = char["%"];
     for (let i = 0; i < strfrmt.length - p.length + 1; i++)
         form[i + 1] = strfrmt[i];
@@ -298,7 +298,7 @@ const str_format = function(L) {
         } else { /* format item */
             let form = [];  /* to store the format ('%...') */
             if (++arg > top)
-                lauxlib.luaL_argerror(L, arg, lua.to_luastring("no value"));
+                lauxlib.luaL_argerror(L, arg, lua.to_luastring("no value", true));
             let f = scanformat(L, strfrmt, form);
             strfrmt = f.p;
             form = f.form;
@@ -343,7 +343,7 @@ const str_format = function(L) {
                         concat(b, s);  /* keep entire string */
                         lapi.lua_pop(L, 1);  /* remove result from 'luaL_tolstring' */
                     } else {
-                        lauxlib.luaL_argcheck(L, s.length === strlen(s), arg, lua.to_luastring("string contains zeros"));
+                        lauxlib.luaL_argcheck(L, s.length === strlen(s), arg, lua.to_luastring("string contains zeros", true));
                         if (form.indexOf(char['.']) < 0 && s.length >= 100) {
                             /* no precision and string is too long to be formatted */
                             concat(b, s);  /* keep entire string */
@@ -468,7 +468,7 @@ const getoption = function(h, fmt) {
         case char['c']: {
             r.size = getnum(fmt, -1);
             if (r.size === -1)
-                lauxlib.luaL_error(h.L, lua.to_luastring("missing size for format option 'c'"));
+                lauxlib.luaL_error(h.L, lua.to_luastring("missing size for format option 'c'", true));
             r.opt = KOption.Kchar;
             return r;
         }
@@ -509,13 +509,13 @@ const getdetails = function(h, totalsize, fmt) {
     let align = r.size;  /* usually, alignment follows size */
     if (r.opt === KOption.Kpaddalign) {  /* 'X' gets alignment from following option */
         if (fmt.s[0] === 0)
-            lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'"));
+            lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'", true));
         else {
             let o = getoption(h, fmt);
             align = o.size;
             o = o.opt;
             if (o === KOption.Kchar || align === 0)
-                lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'"));
+                lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'", true));
         }
     }
     if (align <= 1 || r.opt === KOption.Kchar)  /* need no alignment? */
@@ -524,7 +524,7 @@ const getdetails = function(h, totalsize, fmt) {
         if (align > h.maxalign)  /* enforce maximum alignment */
             align = h.maxalign;
         if ((align & (align -1)) !== 0)  /* is 'align' not a power of 2? */
-            lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("format asks for alignment not power of 2"));
+            lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("format asks for alignment not power of 2", true));
         r.ntoalign = (align - (totalsize & (align - 1))) & (align - 1);
     }
     return r;
@@ -585,7 +585,7 @@ const str_pack = function(L) {
                 let n = lauxlib.luaL_checkinteger(L, arg);
                 if (size < SZINT) {  /* need overflow check? */
                     let lim = 1 << (size * 8) - 1;
-                    lauxlib.luaL_argcheck(L, -lim <= n && n < lim, arg, lua.to_luastring("integer overflow"));
+                    lauxlib.luaL_argcheck(L, -lim <= n && n < lim, arg, lua.to_luastring("integer overflow", true));
                 }
                 packint(b, n, h.islittle, size, n < 0);
                 break;
@@ -593,7 +593,7 @@ const str_pack = function(L) {
             case KOption.Kuint: {  /* unsigned integers */
                 let n = lauxlib.luaL_checkinteger(L, arg);
                 if (size < SZINT)
-                    lauxlib.luaL_argcheck(L, n < (1 << (size * NB)), arg, lua.to_luastring("unsigned overflow"));
+                    lauxlib.luaL_argcheck(L, n < (1 << (size * NB)), arg, lua.to_luastring("unsigned overflow", true));
                 packint(b, n, h.islittle, size, false);
                 break;
             }
@@ -605,7 +605,7 @@ const str_pack = function(L) {
             case KOption.Kchar: {  /* fixed-size string */
                 let s = lauxlib.luaL_checkstring(L, arg);
                 let len = s.length;
-                lauxlib.luaL_argcheck(L, len <= size, arg, lua.to_luastring("string long than given size"));
+                lauxlib.luaL_argcheck(L, len <= size, arg, lua.to_luastring("string long than given size", true));
                 b.push(...s);  /* add string */
                 while (len++ < size)  /* pad extra space */
                     b.push(LUAL_PACKPADBYTE);
@@ -614,7 +614,7 @@ const str_pack = function(L) {
             case KOption.Kstring: {  /* strings with length count */
                 let s = lauxlib.luaL_checkstring(L, arg);
                 let len = s.length;
-                lauxlib.luaL_argcheck(L, size >= NB || len < (1 << size * NB), arg, lua.to_luastring("string length does not fit in given size"));
+                lauxlib.luaL_argcheck(L, size >= NB || len < (1 << size * NB), arg, lua.to_luastring("string length does not fit in given size", true));
                 packint(b, len, h.islittle, size, 0);  /* pack length */
                 b.push(...s);
                 totalsize += len;
@@ -623,7 +623,7 @@ const str_pack = function(L) {
             case KOption.Kzstr: {  /* zero-terminated string */
                 let s = lauxlib.luaL_checkstring(L, arg);
                 let len = s.length;
-                lauxlib.luaL_argcheck(L, s.length === String.fromCharCode(...s).length, arg, lua.to_luastring("strings contains zeros"));
+                lauxlib.luaL_argcheck(L, s.length === String.fromCharCode(...s).length, arg, lua.to_luastring("strings contains zeros", true));
                 b.push(...s);
                 b.push(0);  /* add zero at the end */
                 totalsize += len + 1;
@@ -662,7 +662,7 @@ const str_rep = function(L) {
     let sep = lauxlib.luaL_optstring(L, 3, []);
 
     if (s.length + sep.length < s.length || s.length + sep.length > MAXSIZE / n)  /* may overflow? */
-        return lauxlib.luaL_error(L, lua.to_luastring("resulting string too large"));
+        return lauxlib.luaL_error(L, lua.to_luastring("resulting string too large", true));
 
     let r = [];
     for (let i = 0; i < n - 1; i++)
@@ -683,10 +683,10 @@ const str_byte = function(L) {
     if (pose > l) pose = l;
     if (posi > pose) return 0;  /* empty interval; return no values */
     if (pose - posi >= Number.MAX_SAFE_INTEGER)  /* arithmetic overflow? */
-        return lauxlib.luaL_error(L, lua.to_luastring("string slice too long"));
+        return lauxlib.luaL_error(L, lua.to_luastring("string slice too long", true));
 
     let n = (pose - posi) + 1;
-    lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long"));
+    lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long", true));
     for (let i = 0; i < n; i++)
         lapi.lua_pushinteger(L, s[posi + i - 1]);
     return n;
@@ -707,12 +707,12 @@ const str_packsize = function(L) {
         let size = details.size;
         let ntoalign = details.ntoalign;
         size += ntoalign;  /* total space used by option */
-        lauxlib.luaL_argcheck(L, totalsize <= MAXSIZE - size - 1, lua.to_luastring("format result too large"));
+        lauxlib.luaL_argcheck(L, totalsize <= MAXSIZE - size - 1, lua.to_luastring("format result too large", true));
         totalsize += size;
         switch (opt) {
             case KOption.Kstring:  /* strings with length count */
             case KOption.Kzstr:    /* zero-terminated string */
-                lauxlib.luaL_argerror(L, 1, lua.to_luastring("variable-length format"));
+                lauxlib.luaL_argerror(L, 1, lua.to_luastring("variable-length format", true));
             default:  break;
         }
     }
@@ -771,17 +771,17 @@ const str_unpack = function(L) {
     let ld = data.length;
     let pos = posrelat(lauxlib.luaL_optinteger(L, 3, 1), ld) - 1;
     let n = 0;  /* number of results */
-    lauxlib.luaL_argcheck(L, pos <= ld, 3, lua.to_luastring("initial position out of string"));
+    lauxlib.luaL_argcheck(L, pos <= ld, 3, lua.to_luastring("initial position out of string", true));
     while (fmt.s.length - 1 > 0) {
         let details = getdetails(h, pos, fmt);
         let opt = details.opt;
         let size = details.size;
         let ntoalign = details.ntoalign;
         if (/*ntoalign + size > ~pos ||*/ pos + ntoalign + size > ld)
-            lauxlib.luaL_argerror(L, 2, lua.to_luastring("data string too short"));
+            lauxlib.luaL_argerror(L, 2, lua.to_luastring("data string too short", true));
         pos += ntoalign;  /* skip alignment */
         /* stack space for item + next position */
-        lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many results"));
+        lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many results", true));
         n++;
         switch (opt) {
             case KOption.Kint:
@@ -801,7 +801,7 @@ const str_unpack = function(L) {
             }
             case KOption.Kstring: {
                 let len = unpackint(L, data.slice(pos), h.islittle, size, 0);
-                lauxlib.luaL_argcheck(L, pos + len + size <= ld, 2, lua.to_luastring("data string too short"));
+                lauxlib.luaL_argcheck(L, pos + len + size <= ld, 2, lua.to_luastring("data string too short", true));
                 lapi.lua_pushstring(L, data.slice(pos + size, pos + size + len));
                 pos += len;  /* skip string */
                 break;
@@ -852,21 +852,21 @@ const capture_to_close = function(ms) {
     let level = ms.level;
     for (level--; level >= 0; level--)
         if (ms.capture[level].len === CAP_UNFINISHED) return level;
-    return lauxlib.luaL_error(ms.L, lua.to_luastring("invalid pattern capture"));
+    return lauxlib.luaL_error(ms.L, lua.to_luastring("invalid pattern capture", true));
 };
 
 const classend = function(ms, p) {
     switch(ms.p[p++]) {
         case L_ESC: {
             if (p === ms.p_end)
-                lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (ends with '%')"));
+                lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (ends with '%')", true));
             return p + 1;
         }
         case char['[']: {
             if (ms.p[p] === char['^']) p++;
             do {  /* look for a ']' */
                 if (p === ms.p_end)
-                    lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing ']')"));
+                    lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing ']')", true));
                 if (ms.p[p++] === L_ESC && p < ms.p_end)
                     p++;  /* skip escapes (e.g. '%]') */
             } while (ms.p[p] !== char[']']);
@@ -933,7 +933,7 @@ const singlematch = function(ms, s, p, ep) {
 
 const matchbalance = function(ms, s, p) {
     if (p >= ms.p_end - 1)
-        lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing arguments to '%b'"));
+        lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing arguments to '%b'", true));
     if (ms.src[s] !== ms.p[p])
         return null;
     else {
@@ -976,7 +976,7 @@ const min_expand = function(ms, s, p, ep) {
 
 const start_capture = function(ms, s, p, what) {
     let level = ms.level;
-    if (level >= LUA_MAXCAPTURES) lauxlib.luaL_error(ms.L, lua.to_luastring("too many captures"));
+    if (level >= LUA_MAXCAPTURES) lauxlib.luaL_error(ms.L, lua.to_luastring("too many captures", true));
     ms.capture[level] = ms.capture[level] ? ms.capture[level] : {};
     ms.capture[level].init = s;
     ms.capture[level].len = what;
@@ -1009,7 +1009,7 @@ const match = function(ms, s, p) {
     let gotoinit = true;
 
     if (ms.matchdepth-- === 0)
-        lauxlib.luaL_error(ms.L, lua.to_luastring("pattern too complex"));
+        lauxlib.luaL_error(ms.L, lua.to_luastring("pattern too complex", true));
 
     while (gotoinit || gotodefault) {
         gotoinit = false;
@@ -1118,7 +1118,7 @@ const push_onecapture = function(ms, i, s, e) {
             lauxlib.luaL_error(ms.L, lua.to_luastring(`invalid capture index %${i + 1}`));
     } else {
         let l = ms.capture[i].len;
-        if (l === CAP_UNFINISHED) lauxlib.luaL_error(ms.L, lua.to_luastring("unfinished capture"));
+        if (l === CAP_UNFINISHED) lauxlib.luaL_error(ms.L, lua.to_luastring("unfinished capture", true));
         if (l === CAP_POSITION)
             lapi.lua_pushinteger(ms.L, ms.src_init + 1);
         else
@@ -1128,7 +1128,7 @@ const push_onecapture = function(ms, i, s, e) {
 
 const push_captures = function(ms, s, e) {
     let nlevels = ms.level === 0 && ms.src.slice(s) ? 1 : ms.level;
-    lauxlib.luaL_checkstack(ms.L, nlevels, lua.to_luastring("too many catpures"));
+    lauxlib.luaL_checkstack(ms.L, nlevels, lua.to_luastring("too many catpures", true));
     for (let i = 0; i < nlevels; i++)
         push_onecapture(ms, i, s, e);
     return nlevels;  /* number of strings pushed */
@@ -1340,7 +1340,7 @@ const str_gsub = function(L) {
     let ms = new MatchState(L);
     let b = new lauxlib.luaL_Buffer(L);
     lauxlib.luaL_argcheck(L, tr === CT.LUA_TNUMBER || tr === CT.LUA_TSTRING || tr === CT.LUA_TFUNCTION || tr === CT.LUA_TTABLE, 3,
-        lua.to_luastring("string/function/table expected"));
+        lua.to_luastring("string/function/table expected", true));
     lauxlib.luaL_buffinit(L, b);
     if (anchor) {
         p = p.slice(1); lp--;  /* skip anchor character */
@@ -1392,7 +1392,7 @@ const createmetatable = function(L) {
     lapi.lua_setmetatable(L, -2);  /* set table as metatable for strings */
     lapi.lua_pop(L, 1);  /* pop dummy string */
     lapi.lua_pushvalue(L, -2);  /* get string library */
-    lapi.lua_setfield(L, -2, lua.to_luastring("__index"));  /* metatable.__index = string */
+    lapi.lua_setfield(L, -2, lua.to_luastring("__index", true));  /* metatable.__index = string */
     lapi.lua_pop(L, 1);  /* pop metatable */  
 };
 
diff --git a/src/ltablib.js b/src/ltablib.js
index 210a119..0794887 100644
--- a/src/ltablib.js
+++ b/src/ltablib.js
@@ -36,9 +36,9 @@ const checktab = function(L, arg, what) {
     if (lapi.lua_type(L, arg) !== CT.LUA_TTABLE) {  /* is it not a table? */
         let n = 1;
         if (lapi.lua_getmetatable(L, arg) &&  /* must have metatable */
-            (!(what & TAB_R) || checkfield(L, lua.to_luastring("__index"), ++n)) &&
-            (!(what & TAB_W) || checkfield(L, lua.to_luastring("__newindex"), ++n)) &&
-            (!(what & TAB_L) || checkfield(L, lua.to_luastring("__len"), ++n))) {
+            (!(what & TAB_R) || checkfield(L, lua.to_luastring("__index", true), ++n)) &&
+            (!(what & TAB_W) || checkfield(L, lua.to_luastring("__newindex", true), ++n)) &&
+            (!(what & TAB_L) || checkfield(L, lua.to_luastring("__len", true), ++n))) {
             lapi.lua_pop(L, n);  /* pop metatable and tested metamethods */
         }
         else
@@ -68,7 +68,7 @@ const tinsert = function(L) {
             break;
         case 3: {
             pos = lauxlib.luaL_checkinteger(L, 2);  /* 2nd argument is the position */
-            lauxlib.luaL_argcheck(L, 1 <= pos && pos <= e, 2, lua.to_luastring("position out of bounds"));
+            lauxlib.luaL_argcheck(L, 1 <= pos && pos <= e, 2, lua.to_luastring("position out of bounds", true));
             for (let i = e; i > pos; i--) {  /* move up elements */
                 lapi.lua_geti(L, 1, i - 1);
                 lapi.lua_seti(L, 1, i);  /* t[i] = t[i - 1] */
@@ -76,7 +76,7 @@ const tinsert = function(L) {
             break;
         }
         default: {
-            return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments to 'insert'"));
+            return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments to 'insert'", true));
         }
     }
 
@@ -88,7 +88,7 @@ const tremove = function(L) {
     let size = aux_getn(L, 1, TAB_RW);
     let pos = lauxlib.luaL_optinteger(L, 2, size);
     if (pos !== size)  /* validate 'pos' if given */
-        lauxlib.luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, lua.to_luastring("position out of bounds"));
+        lauxlib.luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, lua.to_luastring("position out of bounds", true));
     lapi.lua_geti(L, 1, pos);  /* result = t[pos] */
     for (; pos < size; pos++) {
         lapi.lua_geti(L, 1, pos + 1);
@@ -113,9 +113,9 @@ const tmove = function(L) {
     checktab(L, 1, TAB_R);
     checktab(L, tt, TAB_W);
     if (e >= f) {  /* otherwise, nothing to move */
-        lauxlib.luaL_argcheck(L, f > 0 || e < llimit.LUA_MAXINTEGER + f, 3, lua.to_luastring("too many elements to move"));
+        lauxlib.luaL_argcheck(L, f > 0 || e < llimit.LUA_MAXINTEGER + f, 3, lua.to_luastring("too many elements to move", true));
         let n = e - f + 1;  /* number of elements to move */
-        lauxlib.luaL_argcheck(L, t <= llimit.LUA_MAXINTEGER - n + 1, 4, lua.to_luastring("destination wrap around"));
+        lauxlib.luaL_argcheck(L, t <= llimit.LUA_MAXINTEGER - n + 1, 4, lua.to_luastring("destination wrap around", true));
 
         if (t > e || t <= f || (tt !== 1 && lapi.lua_compare(L, 1, tt, lua.LUA_OPEQ) !== 1)) {
             for (let i = 0; i < n; i++) {
@@ -173,7 +173,7 @@ const unpack = function(L) {
     if (i > e) return 0;  /* empty range */
     let n = e - i;  /* number of elements minus 1 (avoid overflows) */
     if (n >= Number.MAX_SAFE_INTEGER || !lapi.lua_checkstack(L, ++n))
-        return lauxlib.luaL_error(L, lua.to_luastring("too many results to unpack"));
+        return lauxlib.luaL_error(L, lua.to_luastring("too many results to unpack", true));
     for (; i < e; i++)  /* push arg[i..e - 1] (to avoid overflows) */
         lapi.lua_geti(L, 1, i);
     lapi.lua_geti(L, 1, e);  /* push last element */
@@ -214,7 +214,7 @@ const auxsort = function(L) {
 const sort = function(L) {
     let n = aux_getn(L, 1, TAB_RW);
     if (n > 1) {  /* non-trivial interval? */
-        lauxlib.luaL_argcheck(L, n < Number.MAX_SAFE_INTEGER, 1, lua.to_luastring("array too big"));
+        lauxlib.luaL_argcheck(L, n < Number.MAX_SAFE_INTEGER, 1, lua.to_luastring("array too big", true));
         if (!lapi.lua_isnoneornil(L, 2))  /* is there a 2nd argument? */
             lauxlib.luaL_checktype(L, 2, CT.LUA_TFUNCTION);  /* must be a function */
         lapi.lua_settop(L, 2);  /* make sure there are two arguments */
diff --git a/src/ltm.js b/src/ltm.js
index 4a90f6d..57c2e3f 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -15,30 +15,30 @@ const CT      = lua.constant_types;
 
 
 const TMS = {
-    TM_INDEX:    lua.to_luastring("__index"),
-    TM_NEWINDEX: lua.to_luastring("__newindex"),
-    TM_GC:       lua.to_luastring("__gc"),
-    TM_MODE:     lua.to_luastring("__mode"),
-    TM_LEN:      lua.to_luastring("__len"),
-    TM_EQ:       lua.to_luastring("__eq"),  /* last tag method with fast access */
-    TM_ADD:      lua.to_luastring("__add"),
-    TM_SUB:      lua.to_luastring("__sub"),
-    TM_MUL:      lua.to_luastring("__mul"),
-    TM_MOD:      lua.to_luastring("__mod"),
-    TM_POW:      lua.to_luastring("__pow"),
-    TM_DIV:      lua.to_luastring("__div"),
-    TM_IDIV:     lua.to_luastring("__idiv"),
-    TM_BAND:     lua.to_luastring("__band"),
-    TM_BOR:      lua.to_luastring("__bor"),
-    TM_BXOR:     lua.to_luastring("__bxor"),
-    TM_SHL:      lua.to_luastring("__shl"),
-    TM_SHR:      lua.to_luastring("__shr"),
-    TM_UNM:      lua.to_luastring("__unm"),
-    TM_BNOT:     lua.to_luastring("__bnot"),
-    TM_LT:       lua.to_luastring("__lt"),
-    TM_LE:       lua.to_luastring("__le"),
-    TM_CONCAT:   lua.to_luastring("__concat"),
-    TM_CALL:     lua.to_luastring("__call")
+    TM_INDEX:    lua.to_luastring("__index", true),
+    TM_NEWINDEX: lua.to_luastring("__newindex", true),
+    TM_GC:       lua.to_luastring("__gc", true),
+    TM_MODE:     lua.to_luastring("__mode", true),
+    TM_LEN:      lua.to_luastring("__len", true),
+    TM_EQ:       lua.to_luastring("__eq", true),  /* last tag method with fast access */
+    TM_ADD:      lua.to_luastring("__add", true),
+    TM_SUB:      lua.to_luastring("__sub", true),
+    TM_MUL:      lua.to_luastring("__mul", true),
+    TM_MOD:      lua.to_luastring("__mod", true),
+    TM_POW:      lua.to_luastring("__pow", true),
+    TM_DIV:      lua.to_luastring("__div", true),
+    TM_IDIV:     lua.to_luastring("__idiv", true),
+    TM_BAND:     lua.to_luastring("__band", true),
+    TM_BOR:      lua.to_luastring("__bor", true),
+    TM_BXOR:     lua.to_luastring("__bxor", true),
+    TM_SHL:      lua.to_luastring("__shl", true),
+    TM_SHR:      lua.to_luastring("__shr", true),
+    TM_UNM:      lua.to_luastring("__unm", true),
+    TM_BNOT:     lua.to_luastring("__bnot", true),
+    TM_LT:       lua.to_luastring("__lt", true),
+    TM_LE:       lua.to_luastring("__le", true),
+    TM_CONCAT:   lua.to_luastring("__concat", true),
+    TM_CALL:     lua.to_luastring("__call", true)
 };
 
 const luaT_typenames_ = [
@@ -71,7 +71,7 @@ const luaT_init = function(L) {
 */
 const luaT_objtypename = function(L, o) {
     if ((o.ttistable() && o.metatable !== null) || (o.ttisfulluserdata() && o.metatable !== null)) {
-        let name = o.__index(o, lua.to_luastring('__name'));
+        let name = o.__index(o, lua.to_luastring('__name', true));
         if (name.ttisstring())
             return name.jsstring();
     }
@@ -123,10 +123,10 @@ const luaT_trybinTM = function(L, p1, p2, res, event) {
                 if (n1 !== false && n2 !== false)
                     ldebug.luaG_tointerror(L, p1, p2);
                 else
-                    ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform bitwise operation on"));
+                    ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform bitwise operation on", true));
             }
             default:
-                ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform arithmetic on"));
+                ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform arithmetic on", true));
         }
     }
 };
diff --git a/src/lua.js b/src/lua.js
index 1423750..5175bfb 100644
--- a/src/lua.js
+++ b/src/lua.js
@@ -140,9 +140,16 @@ class lua_Debug {
 
 }
 
-const to_luastring = function(str, maxBytesToWrite) {
+const to_luastring_cache = {};
+
+const to_luastring = function(str, cache, maxBytesToWrite) {
     assert(typeof str === "string", "to_luastring expect a js string");
 
+    if (cache) {
+        let cached = to_luastring_cache[str];
+        if (Array.isArray(cached)) return cached;
+    }
+
     maxBytesToWrite = maxBytesToWrite !== undefined ? maxBytesToWrite : Number.MAX_SAFE_INTEGER;
     let outU8Array = [];
 
@@ -195,6 +202,8 @@ const to_luastring = function(str, maxBytesToWrite) {
     }
     // Null-terminate the pointer to the buffer.
     // outU8Array[outIdx] = 0;
+
+    if (cache) to_luastring_cache[str] = outU8Array;
     return outU8Array;
 };
 
diff --git a/src/lutf8lib.js b/src/lutf8lib.js
index ef9739f..f58223a 100644
--- a/src/lutf8lib.js
+++ b/src/lutf8lib.js
@@ -87,7 +87,7 @@ const utflen = function(L) {
 
 const pushutfchar = function(L, arg) {
     let code = lauxlib.luaL_checkinteger(L, arg);
-    lauxlib.luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, lua.to_luastring("value out of range"));
+    lauxlib.luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, lua.to_luastring("value out of range", true));
     lapi.lua_pushstring(L, lua.to_luastring(String.fromCharCode(code)));
 };
 
@@ -121,14 +121,14 @@ const byteoffset = function(L) {
     let posi = n >= 0 ? 1 : s.length + 1;
     posi = u_posrelat(lauxlib.luaL_optinteger(L, 3, posi), s.length);
 
-    lauxlib.luaL_argcheck(L, 1 <= posi && --posi <= s.length, 3, lua.to_luastring("position ot ouf range"));
+    lauxlib.luaL_argcheck(L, 1 <= posi && --posi <= s.length, 3, lua.to_luastring("position ot ouf range", true));
 
     if (n === 0) {
         /* find beginning of current byte sequence */
         while (posi > 0 && iscont(s[posi])) posi--;
     } else {
         if (iscont(s[posi]))
-            lauxlib.luaL_error(L, lua.to_luastring("initial position is a continuation byte"));
+            lauxlib.luaL_error(L, lua.to_luastring("initial position is a continuation byte", true));
 
         if (n < 0) {
             while (n < 0 && posi > 0) {  /* move back */
@@ -166,19 +166,19 @@ const codepoint = function(L) {
     let posi = u_posrelat(lauxlib.luaL_optinteger(L, 2, 1), s.length);
     let pose = u_posrelat(lauxlib.luaL_optinteger(L, 3, posi), s.length);
 
-    lauxlib.luaL_argcheck(L, posi >= 1, 2, lua.to_luastring("out of range"));
-    lauxlib.luaL_argcheck(L, pose <= s.length, 3, lua.to_luastring("out of range"));
+    lauxlib.luaL_argcheck(L, posi >= 1, 2, lua.to_luastring("out of range", true));
+    lauxlib.luaL_argcheck(L, pose <= s.length, 3, lua.to_luastring("out of range", true));
 
     if (posi > pose) return 0;  /* empty interval; return no values */
     if (pose - posi >= Number.MAX_SAFE_INTEGER)
-        return lauxlib.luaL_error(L, lua.to_luastring("string slice too long"));
+        return lauxlib.luaL_error(L, lua.to_luastring("string slice too long", true));
     let n = (pose - posi) + 1;
-    lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long"));
+    lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long", true));
     n = 0;
     for (s = s.slice(posi - 1); n < pose - posi;) {
         let dec = utf8_decode(s);
         if (dec === null)
-            return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code"));
+            return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code", true));
         s = dec.string;
         let code = dec.code;
         lapi.lua_pushinteger(L, code);
@@ -207,7 +207,7 @@ const iter_aux = function(L) {
         let code = dec ? dec.code : null;
         let next = dec ? dec.string : null;
         if (next === null || iscont(next[0]))
-            return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code"));
+            return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code", true));
         lapi.lua_pushinteger(L, n + 1);
         lapi.lua_pushinteger(L, code);
         return 2;
@@ -236,7 +236,7 @@ const UTF8PATT = "[\0-\x7F\xC2-\xF4][\x80-\xBF]*";
 const luaopen_utf8 = function(L) {
     lauxlib.luaL_newlib(L, funcs);
     lapi.lua_pushstring(L, lua.to_luastring(UTF8PATT));
-    lapi.lua_setfield(L, -2, lua.to_luastring("charpattern"));
+    lapi.lua_setfield(L, -2, lua.to_luastring("charpattern", true));
     return 1;
 };
 
diff --git a/src/lvm.js b/src/lvm.js
index efbb7e7..1d131cf 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -602,19 +602,19 @@ const luaV_execute = function(L) {
                     let nstep = tonumber(pstep);
 
                     if (nlimit === false)
-                        ldebug.luaG_runerror(L, lua.to_luastring("'for' limit must be a number"));
+                        ldebug.luaG_runerror(L, lua.to_luastring("'for' limit must be a number", true));
 
                     plimit.type = CT.LUA_TNUMFLT;
                     plimit.value = nlimit;
 
                     if (nstep === false)
-                        ldebug.luaG_runerror(L, lua.to_luastring("'for' step must be a number"));
+                        ldebug.luaG_runerror(L, lua.to_luastring("'for' step must be a number", true));
 
                     pstep.type = CT.LUA_TNUMFLT;
                     pstep.value = nstep;
 
                     if (ninit === false)
-                        ldebug.luaG_runerror(L, lua.to_luastring("'for' initial value must be a number"));
+                        ldebug.luaG_runerror(L, lua.to_luastring("'for' initial value must be a number", true));
 
                     init.type = CT.LUA_TNUMFLT;
                     init.value = ninit - nstep;
@@ -988,7 +988,7 @@ const luaV_objlen = function(L, ra, rb) {
         default: {
             tm = ltm.luaT_gettmbyobj(L, rb, ltm.TMS.TM_LEN);
             if (tm.ttisnil())
-                ldebug.luaG_typeerror(L, rb, lua.to_luastring("get length of"));
+                ldebug.luaG_typeerror(L, rb, lua.to_luastring("get length of", true));
             break;
         }
     }
@@ -1053,7 +1053,7 @@ const gettable = function(L, table, key, ra, recur) {
     recur = recur ? recur : 0;
 
     if (recur >= MAXTAGRECUR)
-        ldebug.luaG_runerror(L, lua.to_luastring("'__index' chain too long; possible loop"));
+        ldebug.luaG_runerror(L, lua.to_luastring("'__index' chain too long; possible loop", true));
 
     if (table.ttistable()) {
         let element = table.__index(table, key);
@@ -1074,7 +1074,7 @@ const luaV_finishget = function(L, t, key, val, slot, recur) {
         assert(!t.ttistable());
         tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX);
         if (tm.ttisnil())
-            ldebug.luaG_typeerror(L, t, lua.to_luastring('index'));
+            ldebug.luaG_typeerror(L, t, lua.to_luastring('index', true));
     } else { /* 't' is a table */
         assert(slot.ttisnil());
         tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX); // TODO: fasttm
@@ -1096,7 +1096,7 @@ const settable = function(L, table, key, v, recur) {
     recur = recur ? recur : 0;
 
     if (recur >= MAXTAGRECUR)
-        ldebug.luaG_runerror(L, lua.to_luastring("'__newindex' chain too long; possible loop"));
+        ldebug.luaG_runerror(L, lua.to_luastring("'__newindex' chain too long; possible loop", true));
 
     if (table.ttistable()) {
         let element = table.__index(table, key);
@@ -1123,7 +1123,7 @@ const luaV_finishset = function(L, t, key, val, slot, recur) {
     } else { /* not a table; check metamethod */
         tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_NEWINDEX);
         if (tm.ttisnil())
-            ldebug.luaG_typeerror(L, t, lua.to_luastring('index'));
+            ldebug.luaG_typeerror(L, t, lua.to_luastring('index', true));
     }
 
     if (tm.ttisfunction()) {
-- 
cgit v1.2.3-70-g09d2