aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <giann@users.noreply.github.com>2017-05-23 07:58:09 +0200
committerGitHub <noreply@github.com>2017-05-23 07:58:09 +0200
commit412b24a06c681a03ab1c1f84e00142cab40bf602 (patch)
treea8be2dfdf82a0b5a02da52cb08175c2b1f3698b9
parentb2fb7a9302693fe5e164469eedb802b54e460347 (diff)
parentbeb5d0abd707c112953719a8ac077dff88a5ecf7 (diff)
downloadfengari-412b24a06c681a03ab1c1f84e00142cab40bf602.tar.gz
fengari-412b24a06c681a03ab1c1f84e00142cab40bf602.tar.bz2
fengari-412b24a06c681a03ab1c1f84e00142cab40bf602.zip
Merge pull request #64 from daurnimator/lightuserdata-keys
Fix collisions with lightuserdata table keys
-rw-r--r--src/lcode.js6
-rw-r--r--src/lstring.js3
-rw-r--r--src/ltable.js53
3 files changed, 54 insertions, 8 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);
};
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..b408cd1 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:
@@ -17,9 +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_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:
case CT.LUA_TLCF:
@@ -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);
}
@@ -127,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;
@@ -153,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) {