From b62bcdfa67d6b0359bf45930ab392953d69eb399 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Sat, 18 Feb 2017 16:07:47 +0100 Subject: setmetatable, getmetatable --- src/lapi.js | 31 ++++++++++++++++++++++++++++++- src/lauxlib.js | 35 +++++++++++++++++++++++++++++++++-- src/lbaselib.js | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 94 insertions(+), 8 deletions(-) (limited to 'src') 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; -- cgit v1.2.3-70-g09d2