summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--src/lapi.js31
-rw-r--r--src/lauxlib.js35
-rw-r--r--src/lbaselib.js36
-rw-r--r--tests/lapi.js23
-rw-r--r--tests/lbaselib.js85
6 files changed, 183 insertions, 33 deletions
diff --git a/README.md b/README.md
index 3407512..19306fe 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@
- [x] lua_getfield
- [x] lua_getglobal
- [x] lua_getmetatable
+ - [x] lua_setmetatable
- [x] lua_pushglobaltable
- [x] lua_pushliteral
- [x] lua_rawget
@@ -124,7 +125,6 @@
- [ ] lua_sethook
- [ ] lua_seti
- [ ] lua_setlocal
- - [ ] lua_setmetatable
- [ ] lua_setupvalue
- [ ] lua_setuservalue
- [ ] lua_status
@@ -142,6 +142,7 @@
- [x] luaL_newstate
- [x] luaL_typename
- [x] luaL_checkany
+ - [x] luaL_checktype
- [x] luaL_callmeta
- [x] luaL_getmetafield
- [x] luaL_setfuncs
@@ -167,7 +168,6 @@
- [ ] luaL_checknumber
- [ ] luaL_checkoption
- [ ] luaL_checkstring
- - [ ] luaL_checktype
- [ ] luaL_checkudata
- [ ] luaL_checkversion
- [ ] luaL_dofile
@@ -204,6 +204,8 @@
- [ ] Standard library
- [x] tostring
- [x] print
+ - [x] getmetatable
+ - [x] setmetatable
- [ ] ...
- [ ] Debug (errors)
- [ ] DOM API binding
diff --git a/src/lapi.js b/src/lapi.js
index ece02e0..e1e548d 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -260,6 +260,33 @@ const lua_setglobal = function(L, name) {
auxsetstr(L, L.l_G.l_registry.value.array[lua.LUA_RIDX_GLOBALS - 1], name);
};
+const lua_setmetatable = function(L, objindex) {
+ assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack");
+ let mt;
+ let obj = index2addr(L, objindex);
+ if (L.stack[L.top - 1].ttisnil())
+ mt = null;
+ else {
+ assert(L.stack[L.top - 1].ttistable(), "table expected");
+ mt = L.stack[L.top - 1];
+ }
+
+ switch (obj.ttnov()) {
+ case CT.LUA_TUSERDATA:
+ case CT.LUA_TTABLE: {
+ obj.metatable = mt;
+ break;
+ }
+ default: {
+ L.l_G.mt[obj.ttnov()] = mt;
+ break;
+ }
+ }
+
+ L.top--;
+ return true;
+};
+
const lua_settable = function(L, idx) {
assert(2 < L.top - L.ci.funcOff, "not enough elements in the stack");
@@ -562,4 +589,6 @@ module.exports.lua_pushglobaltable = lua_pushglobaltable;
module.exports.lua_setfield = lua_setfield;
module.exports.lua_getfield = lua_getfield;
module.exports.lua_getglobal = lua_getglobal;
-module.exports.lua_getmetatable = lua_getmetatable; \ No newline at end of file
+module.exports.lua_getmetatable = lua_getmetatable;
+module.exports.lua_setmetatable = lua_setmetatable;
+module.exports.lua_settop = lua_settop; \ No newline at end of file
diff --git a/src/lauxlib.js b/src/lauxlib.js
index 6db0aae..a1f9d64 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -13,7 +13,27 @@ const LUA_LOADED_TABLE = "_LOADED"
const panic = function(L) {
console.log(`PANIC: unprotected error in call to Lua API (...)`);
return 0;
-}
+};
+
+const typeerror = function(L, arg, tname) {
+ let typearg;
+ if (luaL_getmetafield(L, arg, "__name") === CT.LUA_TSTRING)
+ typearg = lapi.lua_tostring(L, -1);
+ else if (lapi.lua_type(L, arg) === CT.LUA_TLIGHTUSERDATA)
+ typearg = "light userdata";
+ else
+ typearg = luaL_typename(L, arg);
+
+ throw new Error(`${tname} expected, got ${typearg}`);
+
+ // TODO:
+ // let msg = lua_pushstring(L, `${tname} expected, got ${typearg}`);
+ // return luaL_argerror(L, arg, msg);
+};
+
+const tag_error = function(L, arg, tag) {
+ typeerror(L, arg, lapi.lua_typename(L, tag));
+};
const luaL_newstate = function() {
let L = lstate.lua_newstate();
@@ -26,11 +46,20 @@ const luaL_typename = function(L, i) {
return lapi.lua_typename(L, lapi.lua_type(L, i));
};
+const luaL_argcheck = function(L, cond, arg, extramsg) {
+ if (!cond) throw new Error("bad argument"); // TODO: luaL_argerror
+};
+
const luaL_checkany = function(L, arg) {
if (lapi.lua_type(L, arg) === CT.LUA_TNONE)
throw new Error("value expected"); // TODO: luaL_argerror(L, arg, "value expected");
};
+const luaL_checktype = function(L, arg, t) {
+ if (lapi.lua_type(L, arg) !== t)
+ tag_error(L, arg, t);
+};
+
const luaL_getmetafield = function(L, obj, event) {
if (!lapi.lua_getmetatable(L, obj))
return CT.LUA_TNIL;
@@ -157,6 +186,7 @@ const luaL_checkstack = function(L, space, msg) {
module.exports.luaL_newstate = luaL_newstate;
module.exports.luaL_typename = luaL_typename;
module.exports.luaL_checkany = luaL_checkany;
+module.exports.luaL_checktype = luaL_checktype;
module.exports.luaL_callmeta = luaL_callmeta;
module.exports.luaL_getmetafield = luaL_getmetafield;
module.exports.luaL_requiref = luaL_requiref;
@@ -164,4 +194,5 @@ module.exports.luaL_getsubtable = luaL_getsubtable;
module.exports.luaL_setfuncs = luaL_setfuncs;
module.exports.luaL_checkstack = luaL_checkstack;
module.exports.LUA_LOADED_TABLE = LUA_LOADED_TABLE;
-module.exports.luaL_tolstring = luaL_tolstring; \ No newline at end of file
+module.exports.luaL_tolstring = luaL_tolstring;
+module.exports.luaL_argcheck = luaL_argcheck; \ No newline at end of file
diff --git a/src/lbaselib.js b/src/lbaselib.js
index b046f67..12c28fe 100644
--- a/src/lbaselib.js
+++ b/src/lbaselib.js
@@ -6,6 +6,7 @@ const assert = require('assert');
const lua = require('./lua.js');
const lapi = require('./lapi.js');
const lauxlib = require('./lauxlib.js');
+const CT = lua.constant_types;
const luaB_print = function(L) {
let n = lapi.lua_gettop(L); /* number of arguments */
@@ -35,9 +36,32 @@ const luaB_tostring = function(L) {
return true;
};
+const luaB_getmetatable = function(L) {
+ lauxlib.luaL_checkany(L, 1);
+ if (!lapi.lua_getmetatable(L, 1)) {
+ lapi.lua_pushnil(L);
+ return true; /* no metatable */
+ }
+ lauxlib.luaL_getmetafield(L, 1, "__metatable");
+ return true; /* 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, "nil or table expected");
+ if (lauxlib.luaL_getmetafield(L, 1, "__metatable") !== CT.LUA_TNIL)
+ throw new Error("cannot change a protected metatable");
+ lapi.lua_settop(L, 2);
+ lapi.lua_setmetatable(L, 1);
+ return true;
+};
+
const base_funcs = {
- "print": luaB_print,
- "tostring": luaB_tostring,
+ "print": luaB_print,
+ "tostring": luaB_tostring,
+ "getmetatable": luaB_getmetatable,
+ "setmetatable": luaB_setmetatable,
};
const luaopen_base = function(L) {
@@ -53,6 +77,8 @@ const luaopen_base = function(L) {
return true;
};
-module.exports.luaB_tostring = luaB_tostring;
-module.exports.luaB_print = luaB_print;
-module.exports.luaopen_base = luaopen_base;
+module.exports.luaB_tostring = luaB_tostring;
+module.exports.luaB_print = luaB_print;
+module.exports.luaB_getmetatable = luaB_getmetatable;
+module.exports.luaB_setmetatable = luaB_setmetatable;
+module.exports.luaopen_base = luaopen_base;
diff --git a/tests/lapi.js b/tests/lapi.js
index 561b774..30366d5 100644
--- a/tests/lapi.js
+++ b/tests/lapi.js
@@ -490,27 +490,4 @@ test('lua_settable, lua_gettable', function (t) {
"value",
"Correct element(s) on the stack"
);
-});
-
-
-test('print', function (t) {
- let luaCode = `
- print("hello", "world", 123)
- `, L;
-
- t.plan(1);
-
- t.doesNotThrow(function () {
-
- let bc = toByteCode(luaCode).dataView;
-
- L = lauxlib.luaL_newstate();
-
- linit.luaL_openlibs(L);
-
- lapi.lua_load(L, bc, "test-lua_load")
-
- lapi.lua_call(L, 0, 1);
-
- }, "JS Lua program ran without error");
}); \ No newline at end of file
diff --git a/tests/lbaselib.js b/tests/lbaselib.js
new file mode 100644
index 0000000..3ddec2d
--- /dev/null
+++ b/tests/lbaselib.js
@@ -0,0 +1,85 @@
+/*jshint esversion: 6 */
+"use strict";
+
+const test = require('tape');
+const beautify = require('js-beautify').js_beautify;
+
+const tests = require("./tests.js");
+const getState = tests.getState;
+const toByteCode = tests.toByteCode;
+
+const VM = require("../src/lvm.js");
+const ldo = require("../src/ldo.js");
+const lapi = require("../src/lapi.js");
+const lauxlib = require("../src/lauxlib.js");
+const lua = require('../src/lua.js');
+const linit = require('../src/linit.js');
+const CT = lua.constant_types;
+
+
+test('print', function (t) {
+ let luaCode = `
+ print("hello", "world", 123)
+ `, L;
+
+ t.plan(1);
+
+ t.doesNotThrow(function () {
+
+ let bc = toByteCode(luaCode).dataView;
+
+ L = lauxlib.luaL_newstate();
+
+ linit.luaL_openlibs(L);
+
+ lapi.lua_load(L, bc, "test-print");
+
+ lapi.lua_call(L, 0, 1);
+
+ }, "JS Lua program ran without error");
+});
+
+
+test('setmetatable, getmetatable', function (t) {
+ let luaCode = `
+ local mt = {
+ __index = function ()
+ print("hello")
+ return "hello"
+ end
+ }
+
+ local t = {}
+
+ setmetatable(t, mt);
+
+ return t[1], getmetatable(t)
+ `, L;
+
+ t.plan(3);
+
+ t.doesNotThrow(function () {
+
+ let bc = toByteCode(luaCode).dataView;
+
+ L = lauxlib.luaL_newstate();
+
+ linit.luaL_openlibs(L);
+
+ lapi.lua_load(L, bc, "test-setmetatable-getmetatable");
+
+ lapi.lua_call(L, 0, -1);
+
+ }, "JS Lua program ran without error");
+
+ t.strictEqual(
+ lapi.lua_tostring(L, -2),
+ "hello",
+ "Correct element(s) on the stack"
+ );
+
+ t.ok(
+ lapi.lua_istable(L, -1),
+ "Correct element(s) on the stack"
+ );
+}); \ No newline at end of file