fengari-lua / fengari

🌙 φεγγάρι - The Lua VM written in JS ES6 for Node and the browser

Home Page:https://fengari.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"AssertionError: stack overflow" trying to push a complex JSON object with 10-level nested fields

javierfernandes opened this issue · comments

Hi all !

I run into what seems a limit of the stack size when trying to push a complex object that has a pretty nested structure. Fengari fails with this error

AssertionError: stack overflow
 at Object.lua_pushstring (node_modules/fengari/src/lapi.js:259:5)
    at Object.push (node_modules/fengari-interop/src/jslib.js:71:7)
    at push (src/engine/vm/FengariBNEVM.js:394:15)
    at Array.forEach (<anonymous>)
    at Object.forEach [as _pushObject] (src/engine/vm/FengariBNEVM.js:393:22)
    at Object._pushObject [as _push] (src/engine/vm/FengariBNEVM.js:367:12)
    at _push (src/engine/vm/FengariBNEVM.js:395:12)
    at Array.forEach (<anonymous>)
    at Object.forEach [as _pushObject] (src/engine/vm/FengariBNEVM.js:393:22)
    at Object._pushObject [as _push] (src/engine/vm/FengariBNEVM.js:367:12) thrown

I've tracked the code to all push_ methods which do this

api_check(L, L.top <= L.ci.top, "stack overflow");

I can see that L.ci.top is always 21. And looking at the code I guess that it is some sort of max stack size (?) that I'm reaching. This seems to come from const LUA_MINSTACK = 20;
Here is a test to reproduce this

it('should push an objects with 10 level nested fields', () => {
    expect(vm._getTop()).toBe(1)
    vm._pushObject({
        a: {
          b: {
            c: {
              d: {
                e: {
                  f: {
                    g: {
                      h: {
                        i: {
                          j: 'blah'   // AT THIS LEVEL IT STARTS TO FAIL !
                        }
                      }
                    }
                  }
                },
              }
            },
          },
        }
      })
      expect(vm._getTop()).toBe(2)
    })

vm is just a class that wraps all the fengari calls.
Here all the push methods just in case:

_pushObject(obj) {
    this.lua.lua_newtable(this.L)
    const sub_table_index = this._getTop()

    Object.keys(obj).forEach(fieldName => {
      interop.push(this.L, fieldName)
      this._push(obj[fieldName])
      this.lua.lua_settable(this.L, sub_table_index)
    })
  }

 _push(value) {
    if (Array.isArray(value)) {
      this._pushArray(value)
    } else if (isComplexObject(value)) {
      this._pushObject(value)
    } else {
      this._pushValue(value)
    }
  },

  _pushValue(value) {
    interop.push(this.L, value)
  },

 _pushArray(array) {
    this.lua.lua_newtable(this.L)
    const sub_table_index = this._getTop()

    array.forEach((e, i) => {
      interop.push(this.L, i + 1)
      this._push(e)
      this.lua.lua_settable(this.L, sub_table_index)
    })
  },

Am I reaching a limit here ? If so, is there a way to extend it ? or maybe to do it in a way that it won't hit it, like a workaround ?
Or maybe I'm doing it wrong ?

Thanks in advance !

I think you just need some luaL_checkstack calls at your point of recursion.

@daurnimator thanks !!!
That did the trick !
I didn't know one was supposed to manually make the stack grow to fit what you were going to do next. I'm not so used to working with stack :)

I will close this issue then. I think that it might still help some other like me in the future, since I searched for "stack overflow" without any luck in this repo.