diff --git a/src/vm/wren_compiler.c b/src/vm/wren_compiler.c index 92b16cac0..db86ec4d9 100644 --- a/src/vm/wren_compiler.c +++ b/src/vm/wren_compiler.c @@ -3282,6 +3282,8 @@ static void createConstructor(Compiler* compiler, Signature* signature, Compiler methodCompiler; initCompiler(&methodCompiler, compiler->parser, compiler, true); + methodCompiler.fn->arity = signature->arity; + // Allocate the instance. emitOp(&methodCompiler, compiler->enclosingClass->isForeign ? CODE_FOREIGN_CONSTRUCT : CODE_CONSTRUCT); @@ -3462,6 +3464,8 @@ static bool method(Compiler* compiler, Variable classVariable) error(compiler, "A constructor cannot be static."); } + methodCompiler.fn->arity = signature.arity; + // Include the full signature in debug messages in stack traces. char fullSignature[MAX_METHOD_SIGNATURE]; int length; diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 254d0b037..368270b7c 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -1422,6 +1422,7 @@ WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature) // Create a little stub function that assumes the arguments are on the stack // and calls the method. ObjFn* fn = wrenNewFunction(vm, NULL, numParams + 1); + fn->arity = numParams; // Wrap the function in a closure and then in a handle. Do this here so it // doesn't get collected as we fill it in. @@ -1460,9 +1461,10 @@ WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method) // Discard any extra temporary slots. We take for granted that the stub // function has exactly one slot for each argument. - vm->fiber->stackTop = &vm->fiber->stack[closure->fn->maxSlots]; + const int closureNumArgs = closure->fn->arity + 1; + vm->fiber->stackTop = &vm->fiber->stack[closureNumArgs]; - wrenCallFunction(vm, vm->fiber, closure, 0); + wrenCallFunction(vm, vm->fiber, closure, closureNumArgs); WrenInterpretResult result = runInterpreter(vm, vm->fiber); // If the call didn't abort, then set up the API stack to point to the diff --git a/src/vm/wren_vm.h b/src/vm/wren_vm.h index 7ab74c9ee..d8f6d775f 100644 --- a/src/vm/wren_vm.h +++ b/src/vm/wren_vm.h @@ -187,12 +187,18 @@ static inline void wrenCallFunction(WrenVM* vm, ObjFiber* fiber, fiber->frameCapacity = max; } + // Functions allows to be called with more arguments than required. So the + // the [fiber] [stackTop] has to be realined to match the [closure] [arity]. + const int closureNumArgs = closure->fn->arity + 1; + ASSERT(closureNumArgs <= numArgs, "Expect more arguments"); + fiber->stackTop -= numArgs - closureNumArgs; + // Grow the stack if needed. int stackSize = (int)(fiber->stackTop - fiber->stack); int needed = stackSize + closure->fn->maxSlots; wrenEnsureStack(vm, fiber, needed); - wrenAppendCallFrame(vm, fiber, closure, fiber->stackTop - numArgs); + wrenAppendCallFrame(vm, fiber, closure, fiber->stackTop - closureNumArgs); } // Marks [obj] as a GC root so that it doesn't get collected. diff --git a/test/regression/731.wren b/test/regression/731.wren new file mode 100644 index 000000000..3fd232467 --- /dev/null +++ b/test/regression/731.wren @@ -0,0 +1,5 @@ + +Fn.new { + var foo + System.print(foo) // expect: null +}.call("Bug")