From dbb25922aad020bcf898c08e809811641561cd87 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 23 May 2017 14:31:31 +1000 Subject: src/ltable.js: Implement table_hash for lightuserdata --- src/lstring.js | 3 ++- src/ltable.js | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/lstring.js b/src/lstring.js index 1887e85..a5ee669 100644 --- a/src/lstring.js +++ b/src/lstring.js @@ -27,7 +27,8 @@ const luaS_eqlngstr = function(a, b) { return a == b || (a.realstring.length == b.realstring.length && a.realstring.join() == b.realstring.join()); }; -/* converts strings (arrays) to a consistent map key */ +/* converts strings (arrays) to a consistent map key + make sure this doesn't conflict with any of the anti-collision strategies in ltable */ const luaS_hash = function(str) { assert(Array.isArray(str)); return str.map(e => `${e}|`).join(''); diff --git a/src/ltable.js b/src/ltable.js index c8bf7a2..88b09e3 100644 --- a/src/ltable.js +++ b/src/ltable.js @@ -7,8 +7,20 @@ const defs = require('./defs.js'); const ldebug = require('./ldebug.js'); const lobject = require('./lobject.js'); const lstring = require('./lstring.js'); +const lstate = require('./lstate.js'); const CT = defs.constant_types; +/* used to prevent conflicts with lightuserdata keys */ +let lightuserdata_hashes = new WeakMap(); +const get_lightuserdata_hash = function(v) { + let hash = lightuserdata_hashes.get(v); + if (!hash) { + hash = Symbol("lightuserdata"); + lightuserdata_hashes.set(v, hash); + } + return hash; +}; + const table_hash = function(L, key) { switch(key.type) { case CT.LUA_TNIL: @@ -18,7 +30,6 @@ const table_hash = function(L, key) { return ldebug.luaG_runerror(L, defs.to_luastring("table index is NaN", true)); /* fall through */ case CT.LUA_TBOOLEAN: - case CT.LUA_TLIGHTUSERDATA: /* XXX: if user pushes conflicting lightuserdata then the table will do odd things */ case CT.LUA_TNUMINT: case CT.LUA_TTABLE: case CT.LUA_TLCL: @@ -30,6 +41,35 @@ const table_hash = function(L, key) { case CT.LUA_TSHRSTR: case CT.LUA_TLNGSTR: return lstring.luaS_hashlongstr(key.tsvalue()); + case CT.LUA_TLIGHTUSERDATA: + let v = key.value; + switch(typeof v) { + case "string": + /* possible conflict with LUA_TSTRING. + prefix this string with "*" so they don't clash */ + return "*" + v; + case "number": + /* possible conflict with LUA_TNUMBER. + turn into string and prefix with "#" to avoid clash with other strings */ + return "#" + v; + case "boolean": + /* possible conflict with LUA_TBOOLEAN. use strings ?true and ?false instead */ + return v?"?true":"?false"; + case "function": + /* possible conflict with LUA_TLCF. + indirect via a weakmap */ + return get_lightuserdata_hash(v); + case "object": + /* v shouldn't be a CClosure, LClosure, Table or Userdata from this state as they're never exposed + the only exposed internal type is a lua_State */ + if (v instanceof lstate.lua_State && v.l_G === L.l_G) { + /* indirect via a weakmap */ + return get_lightuserdata_hash(v); + } + /* fall through */ + default: + return v; + } default: throw new Error("unknown key type: " + key.type); } -- cgit v1.2.3-54-g00ecf From fe6129f7edaa3a9dee990cd90f765e7196d992b6 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 23 May 2017 14:34:06 +1000 Subject: src/lcode.js: luaK_intK no longer collides with strings --- src/lcode.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lcode.js b/src/lcode.js index ea010aa..89813cb 100644 --- a/src/lcode.js +++ b/src/lcode.js @@ -470,12 +470,10 @@ const luaK_stringK = function(fs, s) { /* ** Add an integer to list of constants and return its index. ** Integers use userdata as keys to avoid collision with floats with -** same value; conversion to 'void*' is used only for hashing, so there -** are no "precision" problems. +** same value. */ const luaK_intK = function(fs, n) { - /* FIXME: shouldn't use string as key. need to use pointer */ - let k = new TValue(CT.LUA_TLNGSTR, lstring.luaS_bless(fs.L, defs.to_luastring(`${n}`))); + let k = new TValue(CT.LUA_TLIGHTUSERDATA, n); let o = new TValue(CT.LUA_TNUMINT, n); return addk(fs, k, o); }; -- cgit v1.2.3-54-g00ecf From beb5d0abd707c112953719a8ac077dff88a5ecf7 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 23 May 2017 14:54:41 +1000 Subject: src/ltable.js: Convert float keys that fit in an integer to integers --- src/ltable.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ltable.js b/src/ltable.js index 88b09e3..b408cd1 100644 --- a/src/ltable.js +++ b/src/ltable.js @@ -29,8 +29,8 @@ const table_hash = function(L, key) { if (isNaN(key.value)) return ldebug.luaG_runerror(L, defs.to_luastring("table index is NaN", true)); /* fall through */ + case CT.LUA_TNUMINT: /* takes advantage of floats and integers being same in JS */ case CT.LUA_TBOOLEAN: - case CT.LUA_TNUMINT: case CT.LUA_TTABLE: case CT.LUA_TLCL: case CT.LUA_TLCF: @@ -167,6 +167,13 @@ const setgeneric = function(t, hash, key) { if (v) return v.value; + let kv = key.value; + if ((key.ttisfloat() && (kv|0) === kv)) { /* does index fit in an integer? */ + /* insert it as an integer */ + key = new lobject.TValue(CT.LUA_TNUMINT, kv); + } else { + key = new lobject.TValue(key.type, kv); + } let tv = new lobject.TValue(CT.LUA_TNIL, null); add(t, hash, key, tv); return tv; @@ -193,7 +200,7 @@ const luaH_setint = function(t, key, value) { const luaH_set = function(L, t, key) { assert(key instanceof lobject.TValue); let hash = table_hash(L, key); - return setgeneric(t, hash, new lobject.TValue(key.type, key.value)); + return setgeneric(t, hash, key); }; const luaH_delete = function(L, t, key) { -- cgit v1.2.3-54-g00ecf