wren-lang / wren

The Wren Programming Language. Wren is a small, fast, class-based concurrent scripting language.
http://wren.io
MIT License
6.9k stars 552 forks source link

Stack must have enough arguments for method #516

Open sponge opened 6 years ago

sponge commented 6 years ago

I'm not sure if this is similar to #510, but I was running into a similar but separate issue when trying to use wrenInterpret as a REPL. Every time I call wrenInterpret, it succeeds, but later on in the frame, I try and call a saved handle, it will fail with (null):-1 - Null does not implement 'update(_)'.. Usually, the second time I try and wrenInterpret, I hit this assert in wren_vm.c:1316

  ASSERT(vm->fiber->stackTop - vm->fiber->stack >= closure->fn->arity,
         "Stack must have enough arguments for method.");

According to VS, the left hand of the comparison is a huge value: vm->fiber->stackTop - vm->fiber->stack = 2305843009203515350 and arity is 0, which seemed weird (and also not false). I adapted my repro from #510 that will trigger Null does not implement 'update(_)', but can't seem to get the assert to hit on my trivial example

Output:

slot count before call: 5
slot count after call: 1
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
(null):-1 - Null does not implement 'update(_)'.
#include "vm/wren.h"
#include <cassert>

static void wren_error(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message) {
    printf("%s:%i - %s\n", module, line, message);
}

int main()
{
    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.errorFn = wren_error;

    WrenVM *vm = wrenNewVM(&config);

    WrenInterpretResult res = wrenInterpret(vm,
    "class Game { \n\
        construct new(mapName) { \n\
        _i = 0 \n\
        } \n\
        update(dt) { \n\
            _i = _i + 1 \n\
        } \n\
    }");

    assert(res == WREN_RESULT_SUCCESS);

    // make sure we can find a new Game class
    wrenEnsureSlots(vm, 1);
    wrenGetVariable(vm, "main", "Game", 0);
    WrenHandle *gameClass = wrenGetSlotHandle(vm, 0);

    if (gameClass == NULL) {
        abort(0);
    }

    WrenHandle *newHnd = wrenMakeCallHandle(vm, "new(_)");
    WrenHandle *updateHnd = wrenMakeCallHandle(vm, "update(_)");

    // instantiate a new Game
    wrenEnsureSlots(vm, 2);
    wrenSetSlotHandle(vm, 0, gameClass);
    wrenSetSlotString(vm, 1, "constructor string");

    // wrenGetSlotCount returns 2
    printf("slot count before call: %i\n", wrenGetSlotCount(vm));
    wrenCall(vm, newHnd);
    // wrenGetSlotCount returns a random number
    printf("slot count after call: %i\n", wrenGetSlotCount(vm));

    WrenHandle *instanceHnd = wrenGetSlotHandle(vm, 0);

    wrenReleaseHandle(vm, newHnd);
    wrenReleaseHandle(vm, gameClass);

    int i = 0;
    while (i < 100) {
        if (i % 10 == 0) wrenInterpret(vm, "1+2");

        wrenEnsureSlots(vm, 2);
        wrenSetSlotHandle(vm, 0, instanceHnd);
        wrenSetSlotHandle(vm, 0, instanceHnd);
        wrenSetSlotDouble(vm, 1, i);
        wrenCall(vm, updateHnd);
        i++;
    }

    return 0;
}
mhermier commented 6 years ago

It is the same issue, calling the interpreter will force a stack reallocation, that corrupt apiStack.