summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-15 09:22:11 +0100
committerBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-15 17:07:58 +0100
commitc428f1241ebd5194a37c37d9d5376b326b78ee37 (patch)
tree6e0357f728f525da8e27d73bcbc94ec8ce05c645
parentf94e2902d6e2e45840c5a63d46c57ddd888b84b8 (diff)
downloadfengari-c428f1241ebd5194a37c37d9d5376b326b78ee37.tar.gz
fengari-c428f1241ebd5194a37c37d9d5376b326b78ee37.tar.bz2
fengari-c428f1241ebd5194a37c37d9d5376b326b78ee37.zip
Implementing minimal path from main to luaV_execute of user script
-rw-r--r--src/lapi.js214
-rw-r--r--src/ldo.js57
-rw-r--r--src/lfunc.js3
-rw-r--r--src/lobject.js24
-rw-r--r--src/lstate.js14
-rw-r--r--src/lua.js91
-rw-r--r--src/lualib.js11
-rw-r--r--src/lvm.js5
8 files changed, 388 insertions, 31 deletions
diff --git a/src/lapi.js b/src/lapi.js
new file mode 100644
index 0000000..eecf1ab
--- /dev/null
+++ b/src/lapi.js
@@ -0,0 +1,214 @@
+/*jshint esversion: 6 */
+"use strict";
+
+const assert = require('assert');
+const ldo = require('./ldo.js');
+const lobject = require('./lobject.js');
+const lfunc = require('./lfunc.js');
+const lua = require('./lua.js');
+const lstate = require('./lstate.js');
+const nil = ldo.nil;
+const MAXUPVAL = lfunc.MAXUPVAL;
+const CT = lua.constant_types;
+const TS = lua.thread_status;
+const l_isfalse = lobject.l_isfalse;
+const TValue = lobject.TValue;
+const CClosure = lobject.CClosure;
+
+// Return real index on stack
+const index2addr = function(L, idx) {
+ let ci = L.ci;
+ if (idx > 0) {
+ let o = ci.funcOff + idx;
+ assert(idx <= ci.top - (ci.funcOff + 1), "unacceptable index");
+ if (o >= L.top) return nil;
+ else return L.stack[o];
+ } else if (idx < 0) // TODO: pseudo-indices
+ return nil; // TODO: G(L)->l_registry
+ else { /* upvalues */
+ idx = -idx;
+ assert(idx <= MAXUPVAL + 1, "upvalue index too large");
+ if (ci.func.ttislcf()) /* light C function? */
+ return nil; /* it has no upvalues */
+ else {
+ return idx <= ci.func.nupvalues ? ci.func.upvalue[idx - 1] : nil;
+ }
+ }
+
+};
+
+const lua_pushvalue = function(L, idx) {
+ L.stack[L.top] = L.stack[index2addr(L, idx)];
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+};
+
+/*
+** push functions (JS -> stack)
+*/
+
+const lua_pushnil = function(L) {
+ L.stack[L.top] = nil;
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+};
+
+const lua_pushnumber = function(L, n) {
+ assert(typeof n === "number");
+
+ L.stack[L.top] = new TValue(CT.LUA_TNUMFLT, n);
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+};
+
+const lua_pushinteger = function(L, n) {
+ assert(typeof n === "number");
+
+ L.stack[L.top] = new TValue(CT.LUA_TNUMINT, n|0);
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+};
+
+const lua_pushlstring = function(L, s, len) {
+ assert(typeof s === "string");
+ assert(typeof n === "number");
+
+ let ts = len === 0 ? new TValue(CT.LUA_TLNGSTR, "") : new TValue(CT.LUA_TLNGSTR, s.substr(0, len));
+ L.stack[L.top] = ts;
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+
+ return ts.value;
+};
+
+const lua_pushstring = function (L, s) {
+ assert(typeof s === "string");
+ if (!s)
+ L.stack[L.top] = nil;
+ else {
+ let ts = new TValue(CT.LUA_TLNGSTR, s);
+ s = ts.value;
+ }
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+
+ return s;
+};
+
+const lua_pushcclosure = function(L, fn, n) {
+ assert(typeof fn === "function");
+ assert(typeof n === "number");
+
+ if (n === 0)
+ L.stack[L.top] = new TValue(CT.LUA_TLCF, fn);
+ else {
+ assert(n < L.top - L.ci.funcOff, "not enough elements in the stack");
+ assert(n <= MAXUPVAL, "upvalue index too large");
+ let cl = new CClosure(L, n, fn);
+ L.top -= n;
+ while (n--) {
+ cl.upvalue[n] = L.stack[L.top + n];
+ }
+ L.stack[L.top] = cl;
+ }
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+};
+
+const lua_pushboolean = function(L, b) {
+ L.stack[L.top] = new TValue(CT.LUA_TBOOLEAN, b ? true : false);
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+};
+
+const lua_pushlightuserdata = function(L, p) {
+ assert(typeof p === "object");
+
+ L.stack[L.top] = new TValue(CT.LUA_TLIGHTUSERDATA, p);
+
+ L.top++;
+ assert(L.top <= L.ci.top, "stack overflow");
+};
+
+
+/*
+** access functions (stack -> JS)
+*/
+
+const lua_toboolean = function(L, idx) {
+ let o = L.stack[index2addr(L, idx)];
+ return !l_isfalse(o);
+};
+
+const f_call = function(L, ud) {
+ ldo.luaD_callnoyield(L, ud.func, ud.nresults);
+};
+
+
+/*
+** 'load' and 'call' functions (run Lua code)
+*/
+
+const lua_pcallk = function(L, nargs, nresults, errfunc, ctx, k) {
+ assert(nargs + 1 < L.top - L.ci.func, "not enough elements in the stack");
+ assert(L.status === TS.LUA_OK, "cannot do calls on non-normal thread");
+ assert(nargs == lua.LUA_MULTRET || (L.ci.top - L.top >= nargs - nresults, "results from function overflow current stack size"));
+
+ let c = {
+ func: null,
+ funcOff: NaN,
+ nresults: NaN
+ };
+ let status;
+ let func;
+
+ if (errfunc === 0)
+ func = 0;
+ else {
+ let o = L.stack[index2addr(L, errfunc)];
+ // TODO: api_checkstackindex(L, errfunc, o);
+ func = errfunc;
+ }
+
+ c.funcOff = L.top - (nargs + 1); /* function to be called */
+ c.func = L.stack[c.funcOff];
+
+ if (k === null || L.nny > 0) { /* no continuation or no yieldable? */
+ c.nresults = nresults; /* do a 'conventional' protected call */
+ status = ldo.luaD_pcall(L, f_call, c, c.funcOff, c.func);
+ } else { /* prepare continuation (call is already protected by 'resume') */
+ let ci = L.ci;
+ ci.u.c.k = k; /* prepare continuation (call is already protected by 'resume') */
+ ci.u.c.ctx = ctx; /* prepare continuation (call is already protected by 'resume') */
+ /* save information for error recovery */
+ ci.extra = c.funcOff;
+ ci.u.c.old_errfunc = L.errfunc;
+ L.errfunc = c.func;
+ // TODO: setoah(ci->callstatus, L->allowhook);
+ ci.callstatus |= lstate.CIST_YPCALL; /* function can do error recovery */
+ ldo.luaD_call(L, c.funcOff, nresults); /* do the call */
+ ci.callstatus &= ~lstate.CIST_YPCALL;
+ L.errfunc = ci.u.c.old_errfunc;
+ status = TS.LUA_OK;
+ }
+
+ if (nresults == lua.LUA_MULTRET && L.ci.top < L.top)
+ L.ci.top = L.top;
+
+ return status;
+};
+
+module.exports.lua_pushvalue = lua_pushvalue;
+module.exports.lua_pushnil = lua_pushnil;
+module.exports.lua_pushnumber = lua_pushnumber;
+module.exports.lua_pushinteger = lua_pushinteger;
+module.exports.lua_pushlstring = lua_pushlstring;
+module.exports.lua_pushstring = lua_pushstring;
diff --git a/src/ldo.js b/src/ldo.js
index 63b949c..79b92ca 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -2,16 +2,17 @@
"use strict";
const lua = require('./lua.js');
-const CT = lua.constant_types;
-const LUA_MULTRET = lua.LUA_MULTRET;
const lobject = require('./lobject.js');
-const TValue = lobject.TValue;
const lstate = require('./lstate.js');
-const CallInfo = lstate.CallInfo;
const llimit = require('./llimit.js');
const ltm = require('./ltm.js');
-const TMS = ltm.TMS;
const lvm = require('./lvm.js');
+const CT = lua.constant_types;
+const TS = lua.thread_status;
+const LUA_MULTRET = lua.LUA_MULTRET;
+const TValue = lobject.TValue;
+const CallInfo = lstate.CallInfo;
+const TMS = ltm.TMS;
const nil = new TValue(CT.LUA_TNIL, null);
@@ -170,6 +171,49 @@ const luaD_call = function(L, off, nResults) {
L.nCcalls--;
};
+const luaD_rawrunprotected = function(L, f, ud) {
+ let oldnCcalls = L.nCcalls;
+ let lj = { // TODO: necessary when using try/catch ? (ldo.c:47-52)
+ status: TS.LUA_OK,
+ previous: L.errorJmp /* chain new error handler */
+ };
+ L.errorJmp = lj;
+
+ try {
+ f(L, ud);
+ } catch (e) {
+ if (lj.status == 0) lj.status = -1;
+ }
+
+ L.errorJmp = lj.previous;
+ L.nCcalls = oldnCcalls;
+
+ return lj.status;
+
+};
+
+const luaD_pcall = function(L, func, u, old_top, ef) {
+ let old_ci = L.ci;
+ // TODO: lu_byte old_allowhooks = L->allowhook;
+ let old_nny = L.nny;
+ let old_errfunc = L.errfunc;
+ L.errfunc = ef;
+
+ status = luaD_rawrunprotected(L, func, u);
+
+ if (status !== TS.LUA_OK) {
+ lfunc.luaF_close(L, old_top);
+ // TODO: seterrorobj(L, status, oldtop);
+ L.ci = old_ci;
+ // TODO: L->allowhook = old_allowhooks;
+ L.nny = old_nny;
+ }
+
+ L.errfunc = old_errfunc;
+
+ return status;
+};
+
/*
** Similar to 'luaD_call', but does not allow yields during the call
*/
@@ -187,4 +231,5 @@ module.exports.adjust_varargs = adjust_varargs;
module.exports.tryfuncTM = tryfuncTM;
module.exports.stackerror = stackerror;
module.exports.luaD_call = luaD_call;
-module.exports.luaD_callnoyield = luaD_callnoyield; \ No newline at end of file
+module.exports.luaD_callnoyield = luaD_callnoyield;
+module.exports.luaD_pcall = luaD_pcall; \ No newline at end of file
diff --git a/src/lfunc.js b/src/lfunc.js
index 1a1597a..f3e11d3 100644
--- a/src/lfunc.js
+++ b/src/lfunc.js
@@ -91,4 +91,5 @@ const luaF_close = function(L, level) {
module.exports.Proto = Proto;
module.exports.UpVal = UpVal;
module.exports.findupval = findupval;
-module.exports.luaF_close = luaF_close; \ No newline at end of file
+module.exports.luaF_close = luaF_close;
+module.exports.MAXUPVAL = 255; \ No newline at end of file
diff --git a/src/lobject.js b/src/lobject.js
index 725f63d..5860f67 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -189,28 +189,22 @@ class LClosure extends TValue {
}
-class TString extends TValue {
-
- constructor(string) {
- super(CT.LUA_TSTRING, string);
- }
-
-}
+class CClosure extends TValue {
+ constructor(n, f) {
+ super(CT.LUA_TCCL, null);
-class Userdata extends TValue {
-
- constructor(jsObject) {
- super(CT.LUA_TUSERDATA, jsObject);
+ this.f = f;
+ this.nupvalues = n;
+ this.upvalue = new Array(n);
- this.metatable = null;
+ this.value = this;
}
}
module.exports.LClosure = LClosure;
+module.exports.CClosure = CClosure;
module.exports.TValue = TValue;
-module.exports.Table = Table;
-module.exports.TString = TString;
-module.exports.Userdata = Userdata; \ No newline at end of file
+module.exports.Table = Table; \ No newline at end of file
diff --git a/src/lstate.js b/src/lstate.js
index ed299b5..84aefc0 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -1,7 +1,9 @@
/*jshint esversion: 6 */
"use strict";
-const LUA_MULTRET = require('./lua.js').LUA_MULTRET;
+const lua = require('./lua.js');
+const LUA_MULTRET = lua.LUA_MULTRET;
+const TS = lua.thread_status;
const Table = require('./lobject.js').Table;
class CallInfo {
@@ -14,9 +16,14 @@ class CallInfo {
this.next = next;
this.pcOff = 0;
this.u = {
- l: {
- base: base,
+ l: { /* only for Lua functions */
+ base: base, /* base for this function */
savedpc: []
+ },
+ c: { /* only for JS functions */
+ k: null, /* continuation in case of yields */
+ old_errfunc: null,
+ ctx: null /* context info. in case of yields */
}
};
this.nresults = 0;
@@ -37,6 +44,7 @@ class lua_State {
cl
];
this.openupval = [];
+ this.status = TS.LUA_OK;
}
}
diff --git a/src/lua.js b/src/lua.js
index 33cf623..a336bb0 100644
--- a/src/lua.js
+++ b/src/lua.js
@@ -1,6 +1,32 @@
/*jshint esversion: 6 */
"use strict";
+const assert = require('assert');
+const lualib = require('./lualib.js');
+
+const LUA_VERSION_MAJOR = "5";
+const LUA_VERSION_MINOR = "3";
+const LUA_VERSION_NUM = 503;
+const LUA_VERSION_RELEASE = "4";
+
+const LUA_VERSION = "Lua " + LUA_VERSION_MAJOR + "." + LUA_VERSION_MINOR;
+const LUA_RELEASE = LUA_VERSION + "." + LUA_VERSION_RELEASE;
+const LUA_COPYRIGHT = LUA_RELEASE + " Copyright (C) 1994-2017 Lua.org, PUC-Rio";
+const LUA_AUTHORS = "R. Ierusalimschy, L. H. de Figueiredo, W. Celes";
+
+const FENGARI_VERSION_MAJOR = "0";
+const FENGARI_VERSION_MINOR = "0";
+const FENGARI_VERSION_NUM = 1;
+const FENGARI_VERSION_RELEASE = "1";
+
+const FENGARI_VERSION = "Fengari " + FENGARI_VERSION_MAJOR + "." + FENGARI_VERSION_MINOR;
+const FENGARI_RELEASE = FENGARI_VERSION + "." + FENGARI_VERSION_RELEASE;
+const FENGARI_COPYRIGHT = FENGARI_RELEASE + " Copyright (C) 2017 BenoƮt Giannangeli\nBased on: " + LUA_COPYRIGHT;
+const FENGARI_AUTHORS = "B. Giannangeli";
+
+const LUA_INIT_VAR = "LUA_INIT";
+const LUA_INITVARVERSION = LUA_INIT_VAR + lualib.LUA_VERSUFFIX;
+
const thread_status = {
LUA_OK: 0,
LUA_YIELD: 1,
@@ -35,6 +61,65 @@ constant_types.LUA_TLCL = constant_types.LUA_TFUNCTION | (0 << 4); /* Lua closu
constant_types.LUA_TLCF = constant_types.LUA_TFUNCTION | (1 << 4); /* light C function */
constant_types.LUA_TCCL = constant_types.LUA_TFUNCTION | (2 << 4); /* C closure */
-module.exports.constant_types = constant_types;
-module.exports.thread_status = thread_status;
-module.exports.LUA_MULTRET = -1; \ No newline at end of file
+const print_version = function() {
+ console.log(FENGARI_COPYRIGHT);
+};
+
+
+const handle_script = function(L, args) {
+ // TODO: stdin
+
+};
+
+const handle_luainit = function(L) {
+ // TODO: Should execute script in LUA_INIT_5_3
+ return thread_status.LUA_OK;
+};
+
+/*
+** Main body of stand-alone interpreter (to be called in protected mode).
+** Reads the options and handles them all.
+*/
+const pmain = function(L) {
+ // arguments are a userdata wrapping a plain JS object
+ let args = L.stack[1].value; // For now it should only hold a DataView containing bytecode
+
+ // TODO: luaL_checkversion(L);
+ // TODO: LUA_NOENV
+ // TODO: luaL_openlibs(L);
+ // TODO: createargtable(L, argv, argc, script);
+
+ if (!args.E) {
+ if (handle_luainit(L) != thread_status.LUA_OK)
+ return 0; /* error running LUA_INIT */
+ }
+
+ // TODO: runargs(L, argv, script)
+ if (args.script && handle_script(L, args) != thread_status.LUA_OK)
+ return 0;
+
+ // TODO: doREPL(L);
+};
+
+module.exports.constant_types = constant_types;
+module.exports.thread_status = thread_status;
+module.exports.LUA_MULTRET = -1;
+module.exports.print_version = print_version;
+module.exports.LUA_VERSION_MAJOR = LUA_VERSION_MAJOR;
+module.exports.LUA_VERSION_MINOR = LUA_VERSION_MINOR;
+module.exports.LUA_VERSION_NUM = LUA_VERSION_NUM;
+module.exports.LUA_VERSION_RELEASE = LUA_VERSION_RELEASE;
+module.exports.LUA_VERSION = LUA_VERSION;
+module.exports.LUA_RELEASE = LUA_RELEASE;
+module.exports.LUA_COPYRIGHT = LUA_COPYRIGHT;
+module.exports.LUA_AUTHORS = LUA_AUTHORS;
+module.exports.FENGARI_VERSION_MAJOR = FENGARI_VERSION_MAJOR;
+module.exports.FENGARI_VERSION_MINOR = FENGARI_VERSION_MINOR;
+module.exports.FENGARI_VERSION_NUM = FENGARI_VERSION_NUM;
+module.exports.FENGARI_VERSION_RELEASE = FENGARI_VERSION_RELEASE;
+module.exports.FENGARI_VERSION = FENGARI_VERSION;
+module.exports.FENGARI_RELEASE = FENGARI_RELEASE;
+module.exports.FENGARI_COPYRIGHT = FENGARI_COPYRIGHT;
+module.exports.FENGARI_AUTHORS = FENGARI_AUTHORS;
+module.exports.LUA_INIT_VAR = LUA_INIT_VAR;
+module.exports.LUA_INITVARVERSION = LUA_INITVARVERSION; \ No newline at end of file
diff --git a/src/lualib.js b/src/lualib.js
new file mode 100644
index 0000000..1eff812
--- /dev/null
+++ b/src/lualib.js
@@ -0,0 +1,11 @@
+/*jshint esversion: 6 */
+"use strict";
+
+const assert = require('assert');
+const lua = require('./lua.js');
+
+
+const LUA_VERSUFFIX = "_" + lua.LUA_VERSION_MAJOR + "_" + lua.LUA_VERSION_MINOR;
+
+
+module.exports.LUA_VERSUFFIX = LUA_VERSUFFIX; \ No newline at end of file
diff --git a/src/lvm.js b/src/lvm.js
index 07a9f96..5ba568d 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -11,7 +11,6 @@ const LUA_MULTRET = lua.LUA_MULTRET;
const lobject = require('./lobject.js');
const TValue = lobject.TValue;
const Table = lobject.Table;
-const TString = lobject.TString;
const LClosure = lobject.LClosure;
const lfunc = require('./lfunc.js');
const UpVal = lfunc.UpVal;
@@ -879,7 +878,7 @@ const tostring = function(L, i) {
let str = `${o.value}`;
if (o.ttisstring() || (o.ttisnumber() && !isNaN(parseFloat(`${str}`)))) {
- L.stack[i] = new TString(str);
+ L.stack[i] = new TValue(CT.LUA_TLNGSTR, str);
return true;
}
@@ -914,7 +913,7 @@ const luaV_concat = function(L, total) {
tl += l;
}
- let ts = new TString("");
+ let ts = new TValue(CT.LUA_TLNGSTR, "");
for (let i = n; i > 0; i--) {
ts.value = `${ts.value}${L.stack[top - i].value}`;
}