Merge pull request #218 from ThakeeNathees/stack-realloc-bug

stack overflow, and realloc bug fixed
This commit is contained in:
Thakee Nathees 2022-05-02 14:25:02 +05:30 committed by GitHub
commit 1d77ec7adf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 35 deletions

View File

@ -170,22 +170,19 @@ void reportCompileTimeError(PKVM* vm, const char* path, int line,
pkByteBufferClear(&buff, vm);
}
void reportRuntimeError(PKVM* vm, Fiber* fiber) {
static void _reportStackFrame(PKVM* vm, CallFrame* frame) {
pkWriteFn writefn = vm->config.stderr_write;
if (writefn == NULL) return;
// Error message.
_printRed(vm, "Error: ");
writefn(vm, fiber->error->data);
writefn(vm, "\n");
// Stack trace.
for (int i = fiber->frame_count - 1; i >= 0; i--) {
CallFrame* frame = &fiber->frames[i];
const Function* fn = frame->closure->fn;
ASSERT(!fn->is_native, OOPS);
int line = fn->fn->oplines.data[frame->ip - fn->fn->opcodes.data - 1];
// After fetching the instruction the ip will be inceased so we're
// reducing it by 1. But stack overflows are occure before executing
// any instruction of that function, so the instruction_index possibly
// be -1 (set it to zero in that case).
int instruction_index = frame->ip - fn->fn->opcodes.data - 1;
if (instruction_index == -1) instruction_index++;
int line = fn->fn->oplines.data[instruction_index];
if (fn->owner->path == NULL) {
@ -208,8 +205,48 @@ void reportRuntimeError(PKVM* vm, Fiber* fiber) {
writefn(vm, buff);
writefn(vm, "]\n");
}
}
void reportRuntimeError(PKVM* vm, Fiber* fiber) {
pkWriteFn writefn = vm->config.stderr_write;
if (writefn == NULL) return;
// Error message.
_printRed(vm, "Error: ");
writefn(vm, fiber->error->data);
writefn(vm, "\n");
// If the stack frames are greater than 2 * max_dump_frames + 1,
// we're only print the first [max_dump_frames] and last [max_dump_frames]
// lines.
int max_dump_frames = 10;
if (fiber->frame_count > 2 * max_dump_frames) {
for (int i = 0; i < max_dump_frames; i++) {
CallFrame* frame = &fiber->frames[fiber->frame_count - 1 - i];
_reportStackFrame(vm, frame);
}
int skipped_count = fiber->frame_count - max_dump_frames * 2;
writefn(vm, " ... skipping ");
char buff[STR_INT_BUFF_SIZE];
sprintf(buff, "%d", skipped_count);
writefn(vm, buff);
writefn(vm, " stack frames\n");
for (int i = max_dump_frames; i >= 0; i--) {
CallFrame* frame = &fiber->frames[i];
_reportStackFrame(vm, frame);
}
} else {
for (int i = fiber->frame_count - 1; i >= 0; i--) {
CallFrame* frame = &fiber->frames[i];
_reportStackFrame(vm, frame);
}
}
}
// Opcode names array.

View File

@ -54,6 +54,11 @@
// header for more information on Nan-tagging.
#define VAR_NAN_TAGGING 1
// The maximum size of the pocketlang stack. This value is arbitrary. currently
// it's 800 KB. Change this to any value upto 2147483647 (signed integer max)
// if you want.
#define MAX_STACK_SIZE 1024 * 800
// The maximum number of argument a pocketlang function supported to call. This
// value is arbitrary and feel free to change it. (Just used this limit for an
// internal buffer to store values before calling a new fiber).

View File

@ -333,8 +333,13 @@ static inline Var importModule(PKVM* vm, String* key) {
void vmEnsureStackSize(PKVM* vm, int size) {
if (size >= (MAX_STACK_SIZE / sizeof(Var))) {
VM_SET_ERROR(vm, newString(vm, "Maximum stack limit reached."));
return;
}
Fiber* fiber = vm->fiber;
if (fiber->stack_size > size) return;
if (fiber->stack_size >= size) return;
int new_size = utilPowerOf2Ceil(size);
@ -378,8 +383,11 @@ void vmEnsureStackSize(PKVM* vm, int size) {
}
}
static inline void pushCallFrame(PKVM* vm, const Closure* closure, Var* rbp) {
// The return address for the next call frame (rbp) has to be set to the
// fiber's ret (fiber->ret == next rbp).
static inline void pushCallFrame(PKVM* vm, const Closure* closure) {
ASSERT(!closure->fn->is_native, OOPS);
ASSERT(vm->fiber->ret != NULL, OOPS);
// Grow the stack frame if needed.
if (vm->fiber->frame_count + 1 > vm->fiber->frame_capacity) {
@ -391,12 +399,12 @@ static inline void pushCallFrame(PKVM* vm, const Closure* closure, Var* rbp) {
}
// Grow the stack if needed.
int needed = (closure->fn->fn->stack_size +
(int)(vm->fiber->sp - vm->fiber->stack));
int current_stack_slots = (int)(vm->fiber->sp - vm->fiber->stack) + 1;
int needed = closure->fn->fn->stack_size + current_stack_slots;
vmEnsureStackSize(vm, needed);
CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++;
frame->rbp = rbp;
frame->rbp = vm->fiber->ret;
frame->closure = closure;
frame->ip = closure->fn->fn->opcodes.data;
@ -927,6 +935,8 @@ L_vm_main_loop:
ASSERT(imported->body != NULL, OOPS);
UPDATE_FRAME(); //< Update the current frame's ip.
// Note that we're setting the main function's return address to the
// module itself (for every other function we'll push a null at the rbp
// before calling them and it'll be returned without modified if the
@ -934,11 +944,11 @@ L_vm_main_loop:
// body of the module, so the main function will return what's at the
// rbp without modifying it. So at the end of the main function the
// stack top would be the module itself.
Var* module_ret = fiber->sp - 1;
fiber->ret = fiber->sp - 1;
pushCallFrame(vm, imported->body);
UPDATE_FRAME(); //< Update the current frame's ip.
pushCallFrame(vm, imported->body, module_ret);
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
CHECK_ERROR(); //< Stack overflow.
}
DISPATCH();
@ -1076,8 +1086,9 @@ L_do_call:
(instruction == OP_SUPER_CALL), OOPS);
UPDATE_FRAME(); //< Update the current frame's ip.
pushCallFrame(vm, closure, fiber->ret);
pushCallFrame(vm, closure);
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
CHECK_ERROR(); //< Stack overflow.
}
}