From 4405575ad986ee0ea652a069bae72d9984cb6a9f Mon Sep 17 00:00:00 2001
From: daurnimator <quae@daurnimator.com>
Date: Thu, 11 May 2017 17:00:12 +1000
Subject: src/lobject.js: Fix dead key TValue type

---
 src/lobject.js | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/lobject.js b/src/lobject.js
index e4ef380..fe23d24 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -13,6 +13,8 @@ const llimit  = require('./llimit.js');
 const CT      = defs.constant_types;
 const char    = defs.char;
 
+const LUA_TPROTO = CT.LUA_NUMTAGS;
+const LUA_TDEADKEY = CT.LUA_NUMTAGS+1;
 
 class TValue {
 
@@ -108,7 +110,7 @@ class TValue {
     }
 
     ttisdeadkey() {
-        return this.checktag(CT.LUA_TDEADKEY);
+        return this.checktag(LUA_TDEADKEY);
     }
 
     l_isfalse() {
@@ -150,6 +152,11 @@ class TValue {
         this.value = x;
     }
 
+    setdeadvalue() {
+        this.type = LUA_TDEADKEY;
+        this.value = null;
+    }
+
     setfrom(tv) { /* in lua C source setobj2t is often used for this */
         this.type = tv.type;
         this.value = tv.value;
@@ -539,6 +546,8 @@ const numarith = function(L, op, v1, v2) {
     }
 };
 
+module.exports.LUA_TPROTO        = LUA_TPROTO;
+module.exports.LUA_TDEADKEY      = LUA_TDEADKEY;
 module.exports.CClosure          = CClosure;
 module.exports.LClosure          = LClosure;
 module.exports.LocVar            = LocVar;
-- 
cgit v1.2.3-70-g09d2


From 6f93b0a3af9e64d3f69a2442af3c21b6fa794ed7 Mon Sep 17 00:00:00 2001
From: daurnimator <quae@daurnimator.com>
Date: Thu, 11 May 2017 17:05:04 +1000
Subject: tests/ltablib.js: Use better filter for numeric keys

---
 tests/ltablib.js | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/tests/ltablib.js b/tests/ltablib.js
index e209db3..43e742b 100644
--- a/tests/ltablib.js
+++ b/tests/ltablib.js
@@ -14,7 +14,7 @@ const inttable2array = function(t) {
     let a = [];
 
     t.strong.forEach(function (v, k) {
-        if (typeof k === 'number')
+        if (v.key.ttisnumber())
             a[k - 1] = v.value;
     });
 
@@ -73,7 +73,7 @@ test('table.pack', function (t) {
 
     t.deepEqual(
         [...lua.lua_topointer(L, -1).strong.entries()]
-            .filter(e => typeof e[0] === 'number') // Filter out the 'n' field
+            .filter(e => e[1].key.ttisnumber()) // Filter out the 'n' field
             .map(e => e[1].value.value).reverse(),
         [1, 2, 3],
         "Correct element(s) on the stack"
@@ -148,7 +148,7 @@ test('table.insert', function (t) {
 
     t.deepEqual(
         [...lua.lua_topointer(L, -1).strong.entries()]
-            .filter(e => typeof e[0] === 'number')
+            .filter(e => e[1].key.ttisnumber())
             .map(e => e[1].value.value).sort(),
         [1, 2, 3, 4, 5],
         "Correct element(s) on the stack"
@@ -182,7 +182,7 @@ test('table.remove', function (t) {
 
     t.deepEqual(
         [...lua.lua_topointer(L, -1).strong.entries()]
-            .filter(e => typeof e[0] === 'number')
+            .filter(e => e[1].key.ttisnumber())
             .map(e => e[1].value.value).sort(),
         [1, 2, 3, 4],
         "Correct element(s) on the stack"
@@ -215,7 +215,7 @@ test('table.move', function (t) {
 
     t.deepEqual(
         [...lua.lua_topointer(L, -1).strong.entries()]
-            .filter(e => typeof e[0] === 'number')
+            .filter(e => e[1].key.ttisnumber())
             .map(e => e[1].value.value).sort(),
         [1, 2, 3, 4, 5, 6],
         "Correct element(s) on the stack"
-- 
cgit v1.2.3-70-g09d2


From a9fe1ab76cb527d8bdafa3071f796b646aada17e Mon Sep 17 00:00:00 2001
From: daurnimator <quae@daurnimator.com>
Date: Thu, 11 May 2017 17:14:31 +1000
Subject: src/ltable.js: luaH_delete can't fully delete immediately as it might
 need dead keys for next()

---
 src/ltable.js | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/ltable.js b/src/ltable.js
index b1f48e8..79c8215 100644
--- a/src/ltable.js
+++ b/src/ltable.js
@@ -34,10 +34,17 @@ class Table {
     constructor(L) {
         this.id = L.l_G.id_counter++;
         this.strong = new Map();
+        this.dead_hashes = [];
         this.metatable = null;
     }
 }
 
+const clean_dead_keys = function(t) {
+    for (let i=0; i<t.dead_hashes.length; i++) {
+        t.strong.delete(t.dead_hashes[i]);
+    }
+};
+
 const luaH_new = function(L) {
     return new Table(L);
 };
@@ -69,6 +76,7 @@ const setgeneric = function(t, hash, key) {
     if (v)
         return v.value;
 
+    clean_dead_keys(t);
     let tv = new lobject.TValue(CT.LUA_TNIL, null);
     t.strong.set(hash, {
         key: key,
@@ -81,7 +89,7 @@ const luaH_setint = function(t, key, value) {
     assert(typeof key == "number" && (key|0) === key && value instanceof lobject.TValue);
     let hash = key; /* table_hash known result */
     if (value.ttisnil()) {
-        t.strong.delete(hash);
+        delete_hash(t, hash);
         return;
     }
     let v = t.strong.get(hash);
@@ -89,6 +97,7 @@ const luaH_setint = function(t, key, value) {
         let tv = v.value;
         tv.setfrom(value);
     } else {
+        clean_dead_keys(t);
         t.strong.set(hash, {
             key: new lobject.TValue(CT.LUA_TNUMINT, key),
             value: new lobject.TValue(value.type, value.value)
@@ -102,10 +111,20 @@ const luaH_set = function(t, key) {
     return setgeneric(t, hash, new lobject.TValue(key.type, key.value));
 };
 
+/* Can't remove from table immediately due to next() */
+const delete_hash = function(t, hash) {
+    let e = t.strong.get(hash);
+    if (e) {
+        e.key.setdeadvalue();
+        e.value = lobject.luaO_nilobject;
+        t.dead_hashes.push(hash);
+    }
+};
+
 const luaH_delete = function(t, key) {
     assert(key instanceof lobject.TValue);
     let hash = table_hash(key);
-    t.strong.delete(hash);
+    return delete_hash(t, hash);
 };
 
 /*
-- 
cgit v1.2.3-70-g09d2


From f42baa3087685417a280abf8bb4283afb9d5f3f9 Mon Sep 17 00:00:00 2001
From: Benoit Giannangeli <giann008@gmail.com>
Date: Thu, 11 May 2017 11:17:11 +0200
Subject: Freeze luaO_nilobject to catch any accidental overwrite

To remove along with asserts calls for any real use
---
 src/lobject.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/lobject.js b/src/lobject.js
index fe23d24..f926e85 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -182,6 +182,7 @@ class TValue {
 }
 
 const luaO_nilobject = new TValue(CT.LUA_TNIL, null);
+Object.freeze(luaO_nilobject);
 module.exports.luaO_nilobject = luaO_nilobject;
 
 class LClosure {
-- 
cgit v1.2.3-70-g09d2


From 57ca9b375d262d98645d219bbdd5969e0c0c85aa Mon Sep 17 00:00:00 2001
From: Benoit Giannangeli <giann008@gmail.com>
Date: Thu, 11 May 2017 11:18:26 +0200
Subject: Don't use luaO_nilobject when deleting table entry

---
 src/ltable.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ltable.js b/src/ltable.js
index 79c8215..52b5aee 100644
--- a/src/ltable.js
+++ b/src/ltable.js
@@ -116,7 +116,7 @@ const delete_hash = function(t, hash) {
     let e = t.strong.get(hash);
     if (e) {
         e.key.setdeadvalue();
-        e.value = lobject.luaO_nilobject;
+        e.value = new lobject.TValue(CT.LUA_TNIL, null);
         t.dead_hashes.push(hash);
     }
 };
-- 
cgit v1.2.3-70-g09d2