diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/lapi.js | 29 | ||||
-rw-r--r-- | src/lbaselib.js | 58 |
3 files changed, 88 insertions, 1 deletions
@@ -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, |