From a2031547aafbb07f6284cd10704435db23d9db60 Mon Sep 17 00:00:00 2001
From: Benoit Giannangeli <benoit.giannangeli@boursorama.fr>
Date: Wed, 8 Feb 2017 07:38:03 +0100
Subject: callstatus, OP_LE, OP_JMP, tests use L.top

---
 src/lobject.js | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/lstate.js  | 14 +++++++--
 src/lvm.js     | 85 ++++++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 179 insertions(+), 10 deletions(-)

(limited to 'src')

diff --git a/src/lobject.js b/src/lobject.js
index 4cc8c4e..3781623 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -12,6 +12,96 @@ class TValue {
         this.metatable = null;
     }
 
+    /* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
+    ttype() {
+        return this.type & 0x3F;
+    }
+
+    /* type tag of a TValue with no variants (bits 0-3) */
+    ttnov() {
+        return this.type & 0x0F;
+    }
+
+    checktag(t) {
+        return this.type === t;
+    }
+    
+    checktype(t) {
+        return this.ttnov() === t;
+    }
+    
+    ttisnumber() {
+        return this.checktype(CT.LUA_TNUMBER);
+    }
+    
+    ttisfloat() {
+        return this.checktag(CT.LUA_TNUMFLT);
+    }
+    
+    ttisinteger() {
+        return this.checktag(CT.LUA_TNUMINT);
+    }
+    
+    ttisnil() {
+        return this.checktag(CT.LUA_TNIL);
+    }
+    
+    ttisboolean() {
+        return this.checktag(CT.LUA_TBOOLEAN);
+    }
+    
+    ttislightuserdata() {
+        return this.checktag(CT.LUA_TLIGHTUSERDATA);
+    }
+    
+    ttisstring() {
+        return this.checktype(CT.LUA_TSTRING);
+    }
+    
+    ttisshrstring() {
+        return this.checktag(ctb(CT.LUA_TSHRSTR));
+    }
+    
+    ttislngstring() {
+        return this.checktag(ctb(CT.LUA_TLNGSTR));
+    }
+    
+    ttistable() {
+        return this.checktag(ctb(CT.LUA_TTABLE));
+    }
+    
+    ttisfunction() {
+        return this.checktype(CT.LUA_TFUNCTION);
+    }
+    
+    ttisclosure() {
+        return (this.type & 0x1F) === CT.LUA_TFUNCTION;
+    }
+    
+    ttisCclosure() {
+        return this.checktag(ctb(CT.LUA_TCCL));
+    }
+    
+    ttisLclosure() {
+        return this.checktag(ctb(CT.LUA_TLCL));
+    }
+    
+    ttislcf() {
+        return this.checktag(CT.LUA_TLCF);
+    }
+    
+    ttisfulluserdata() {
+        return this.checktag(ctb(CT.LUA_TUSERDATA));
+    }
+    
+    ttisthread() {
+        return this.checktag(ctb(CT.LUA_TTHREAD));
+    }
+    
+    ttisdeadkey() {
+        return this.checktag(CT.LUA_TDEADKEY);
+    }
+
 }
 
 
diff --git a/src/lstate.js b/src/lstate.js
index b919f0e..37f2d6b 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -19,6 +19,7 @@ class CallInfo {
             }
         };
         this.nresults = 0;
+        this.callstatus = 0;
     }
 
 }
@@ -40,6 +41,15 @@ class lua_State {
 }
 
 module.exports = {
-    lua_State: lua_State,
-    CallInfo: CallInfo
+    lua_State:       lua_State,
+    CallInfo:        CallInfo,
+    CIST_OAH:        (1<<0),  /* original value of 'allowhook' */
+    CIST_LUA:        (1<<1),  /* call is running a Lua function */
+    CIST_HOOKED:     (1<<2),  /* call is running a debug hook */
+    CIST_FRESH:      (1<<3),  /* call is running on a fresh invocation of luaV_execute */
+    CIST_YPCALL:     (1<<4),  /* call is a yieldable protected call */
+    CIST_TAIL:       (1<<5),  /* call was tail called */
+    CIST_HOOKYIELD:  (1<<6),  /* last hook called yielded */
+    CIST_LEQ:        (1<<7),  /* using __lt for __le */
+    CIST_FIN:        (1<<8)   /* call is running a finalizer */
 };
\ No newline at end of file
diff --git a/src/lvm.js b/src/lvm.js
index d548e77..2574a97 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -14,7 +14,8 @@ const Table          = lobject.Table;
 const LClosure       = lobject.LClosure;
 const lfunc          = require('./lfunc.js');
 const UpVal          = lfunc.UpVal;
-const CallInfo       = require('./lstate.js').CallInfo;
+const lstate         = require('./lstate.js');
+const CallInfo       = lstate.CallInfo;
 
 const nil = new TValue(CT.LUA_TNIL, null);
 
@@ -60,9 +61,11 @@ class LuaVM {
     execute() {
         let L = this.L;
 
+        let ci = L.ci;
+        ci.callstatus |= lstate.CIST_FRESH;
         newframe:
         for (;;) {
-            let ci = L.ci;
+            ci = L.ci;
             var cl = ci.func;
             let k = cl.p.k;
             let base = ci.u.l.base
@@ -340,6 +343,7 @@ class LuaVM {
                     break;
                 }
                 case "OP_JMP": {
+                    this.dojump(ci, i, 0);
                     break;
                 }
                 case "OP_EQ": {
@@ -349,6 +353,11 @@ class LuaVM {
                     break;
                 }
                 case "OP_LE": {
+                    if (LuaVM.luaV_lessequal(this.RKB(base, k, i), this.RKC(base, k, i)) !== i.A)
+                        ci.pcOff++;
+                    else
+                        this.donextjump(ci);
+                    base = ci.u.l.base;
                     break;
                 }
                 case "OP_TEST": {
@@ -397,7 +406,7 @@ class LuaVM {
                         oci.top = L.top;
                         oci.u.l.savedpc = nci.u.l.savedpc;
                         oci.pcOff = nci.pcOff;
-                        //TODO callstatus
+                        oci.callstatus |= lstate.CIST_TAIL;
                         L.ci = oci;
                         ci = L.ci;
                         L.ciOff--;
@@ -410,13 +419,13 @@ class LuaVM {
                 }
                 case "OP_RETURN": {
                     let b = this.postcall(ci, ra, (i.B !== 0 ? i.B - 1 : L.top - ra));
-                    // TODO call status check
-                    ci = L.ci;
-
-                    // TODO what to return when end of program ?
-                    if (L.ci === null) return;
 
+                    if (ci.callstatus & lstate.CIST_FRESH)
+                        return; /* external invocation: return */
+                    
+                    ci = L.ci;
                     if (b) L.top = ci.top;
+
                     continue newframe;
                     break;
                 }
@@ -525,6 +534,7 @@ class LuaVM {
                 ci.top = base + fsize;
                 L.top = ci.top;
                 ci.u.l.savedpc = p.code;
+                ci.callstatus = lstate.CIST_LUA;
                 break;
             }
             default:
@@ -619,6 +629,65 @@ class LuaVM {
         return base;
     }
 
+    dojump(ci, i, e) {
+        let a = i.A;
+        // TODO if (a != 0) luaF_close(L, ci.u.l.base + a - 1);
+        ci.pcOff += i.sBx + e;
+    }
+
+    donextjump(ci) {
+        this.dojump(ci, ci.u.l.savedpc[ci.pcOff], 1);
+    }
+
+    static luaV_lessequal(l, r) {
+        if (l.ttisnumber() && r.ttisnumber())
+            return LuaVM.LEnum(l, r);
+        else if (l.ttisstring() && r.ttisstring())
+            return LuaVM.l_strcmp(l, r) <= 0;
+        // TODO metatable
+        // else if  (l.metatable.__le || r.metatable.__le)
+        //     return l.metatable.__le ? l.metatable.__le(l, r) : r.metatable.__le(l, r);
+        // else {
+        //     L.ci.callstatus |= lstate.CIST_LEQ;
+        //     let res = l.metatable.__lt ? l.metatable.__lt(r, l) : r.metatable.__lt(r, l);
+        //     L.ci.callstatus ^= lstate.CIST_LEQ;
+        //     if (res < 0)
+        //         throw new Error("attempt to compare two string values");
+        //     return !res;
+        // }
+    }
+
+    static LEnum(l, r) {
+        if (l.ttisinteger()) {
+            if (r.ttisinteger())
+                return l.value <= r.value ? 1 : 0;
+            else
+                return LuaVM.LEintfloat(l.value, r.value);
+        } else {
+            if (r.ttisfloat())
+                return l.value <= r.value ? 1 : 0;
+            else if (isNan(l.value))
+                return false;
+            else
+                return !LuaVM.LTintfloat(r.value, l.value);
+        }
+    }
+
+    static LEintfloat(l, r) {
+        // TODO
+        return l <= r ? 1 : 0;
+    }
+
+    static LTintfloat(l, r) {
+        // TODO
+        return l < r ? 1 : 0;
+    }
+
+    static l_strcmp(ls, rs) {
+        // TODO lvm.c:248 static int l_strcmp (const TString *ls, const TString *rs)
+        return ls.value === rs.value ? 0 : (ls.value < rs.value ? -1 : 1);
+    }
+
 }
 
 module.exports = {
-- 
cgit v1.2.3-70-g09d2