aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--src/lapi.js29
-rw-r--r--src/lbaselib.js58
3 files changed, 88 insertions, 1 deletions
diff --git a/README.md b/README.md
index fbc41e7..1637944 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Build Status](https://travis-ci.com/giann/fengari.svg?token=PSYuVp8qrrdszprvDFz7&branch=master)](https://travis-ci.com/giann/fengari) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
+[![Build Status](https://travis-ci.org/giann/fengari.svg?branch=master)](https://travis-ci.org/giann/fengari) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
# fengari
🐺 φεγγάρι - A Lua VM written in JS ES6 targeting the browser
diff --git a/src/lapi.js b/src/lapi.js
index 72ff554..32a9873 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -182,6 +182,11 @@ const lua_insert = function(L, idx) {
lua_rotate(L, idx, 1);
};
+const lua_replace = function(L, idx) {
+ lua_copy(L, -1, idx);
+ lua_pop(L, 1);
+};
+
/*
** push functions (JS -> stack)
*/
@@ -424,6 +429,19 @@ const lua_createtable = function(L, narray, nrec) {
assert(L.top <= L.ci.top, "stack overflow");
};
+const lua_setupvalue = function(L, funcindex, n) {
+ let fi = index2addr(L, funcindex);
+ assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack");
+ let aux = aux_upvalue(fi, n);
+ let name = aux.name;
+ let val = aux.val;
+ if (name) {
+ L.top--;
+ setobj(L, val, L.top);
+ }
+ return name;
+};
+
const lua_newtable = function(L) {
lua_createtable(L, 0, 0);
};
@@ -595,6 +613,14 @@ const lua_typename = function(L, t) {
return ltm.ttypename(t);
};
+const lua_isnil = function(L, n) {
+ return lua_type(L, n) === CT.LUA_TNIL;
+};
+
+const lua_isnone = function(L, n) {
+ return lua_type(L, n) === CT.LUA_TNONE;
+};
+
const lua_isnoneornil = function(L, n) {
return lua_type(L, n) <= 0;
};
@@ -798,6 +824,8 @@ module.exports.lua_gettable = lua_gettable;
module.exports.lua_gettop = lua_gettop;
module.exports.lua_insert = lua_insert;
module.exports.lua_isinteger = lua_isinteger;
+module.exports.lua_isnil = lua_isnil;
+module.exports.lua_isnone = lua_isnone;
module.exports.lua_isnoneornil = lua_isnoneornil;
module.exports.lua_isnumber = lua_isnumber;
module.exports.lua_isstring = lua_isstring;
@@ -830,6 +858,7 @@ module.exports.lua_rawgeti = lua_rawgeti;
module.exports.lua_rawlen = lua_rawlen;
module.exports.lua_rawset = lua_rawset;
module.exports.lua_remove = lua_remove;
+module.exports.lua_replace = lua_replace;
module.exports.lua_rotate = lua_rotate;
module.exports.lua_setfield = lua_setfield;
module.exports.lua_setglobal = lua_setglobal;
diff --git a/src/lbaselib.js b/src/lbaselib.js
index 77638d0..a7666b5 100644
--- a/src/lbaselib.js
+++ b/src/lbaselib.js
@@ -251,6 +251,64 @@ const luaB_xpcall = function(L) {
return finishpcall(L, status, 2);
};
+const load_aux = function(L, status, envidx) {
+ if (status === TS.LUA_OK) {
+ if (envidx !== 0) { /* 'env' parameter? */
+ lapi.lua_pushvalue(L, envidx); /* environment for loaded function */
+ if (!lapi.lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
+ lapi.lua_pop(L, 1); /* remove 'env' if not used by previous call */
+ }
+ return 1;
+ } else { /* error (message is on top of the stack) */
+ lapi.lua_pushnil(L);
+ lapi.lua_insert(L, -2); /* put before error message */
+ return 2; /* return nil plus error message */
+ }
+};
+
+/*
+** reserved slot, above all arguments, to hold a copy of the returned
+** string to avoid it being collected while parsed. 'load' has four
+** optional arguments (chunk, source name, mode, and environment).
+*/
+const RESERVEDSLOT = 5;
+
+/*
+** Reader for generic 'load' function: 'lua_load' uses the
+** stack for internal stuff, so the reader cannot change the
+** stack top. Instead, it keeps its resulting string in a
+** reserved slot inside the stack.
+*/
+const generic_reader = function(L, ud) {
+ lauxlib.luaL_checkstack(L, 2, "too many nested functions");
+ 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, "reader function must return a string");
+ 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, "bt");
+ 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, "=(load)");
+ 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);
+ }
+ return load_aux(L, status, env);
+};
+
const base_funcs = {
"collectgarbage": function () {},
"assert": luaB_assert,