mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 12:46:53 +08:00
stack overflow, and realloc bug fixed
stack realloc wasn't update the next callframe's base pointer which caused a crash which is fixed infinit recursions are now handled properly. now it'll dump the stack frames and exit properly
This commit is contained in:
parent
c05c839c0a
commit
6ac4e93c9a
@ -170,22 +170,19 @@ void reportCompileTimeError(PKVM* vm, const char* path, int line,
|
|||||||
pkByteBufferClear(&buff, vm);
|
pkByteBufferClear(&buff, vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reportRuntimeError(PKVM* vm, Fiber* fiber) {
|
static void _reportStackFrame(PKVM* vm, CallFrame* frame) {
|
||||||
|
|
||||||
pkWriteFn writefn = vm->config.stderr_write;
|
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;
|
const Function* fn = frame->closure->fn;
|
||||||
ASSERT(!fn->is_native, OOPS);
|
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) {
|
if (fn->owner->path == NULL) {
|
||||||
|
|
||||||
@ -208,8 +205,48 @@ void reportRuntimeError(PKVM* vm, Fiber* fiber) {
|
|||||||
writefn(vm, buff);
|
writefn(vm, buff);
|
||||||
writefn(vm, "]\n");
|
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.
|
// Opcode names array.
|
||||||
|
@ -54,6 +54,11 @@
|
|||||||
// header for more information on Nan-tagging.
|
// header for more information on Nan-tagging.
|
||||||
#define VAR_NAN_TAGGING 1
|
#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
|
// 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
|
// 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).
|
// internal buffer to store values before calling a new fiber).
|
||||||
|
29
src/pk_vm.c
29
src/pk_vm.c
@ -333,8 +333,13 @@ static inline Var importModule(PKVM* vm, String* key) {
|
|||||||
|
|
||||||
void vmEnsureStackSize(PKVM* vm, int size) {
|
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;
|
Fiber* fiber = vm->fiber;
|
||||||
if (fiber->stack_size > size) return;
|
if (fiber->stack_size >= size) return;
|
||||||
|
|
||||||
int new_size = utilPowerOf2Ceil(size);
|
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(!closure->fn->is_native, OOPS);
|
||||||
|
ASSERT(vm->fiber->ret != NULL, OOPS);
|
||||||
|
|
||||||
// Grow the stack frame if needed.
|
// Grow the stack frame if needed.
|
||||||
if (vm->fiber->frame_count + 1 > vm->fiber->frame_capacity) {
|
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.
|
// Grow the stack if needed.
|
||||||
int needed = (closure->fn->fn->stack_size +
|
int current_stack_slots = (int)(vm->fiber->sp - vm->fiber->stack) + 1;
|
||||||
(int)(vm->fiber->sp - vm->fiber->stack));
|
int needed = closure->fn->fn->stack_size + current_stack_slots;
|
||||||
vmEnsureStackSize(vm, needed);
|
vmEnsureStackSize(vm, needed);
|
||||||
|
|
||||||
CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++;
|
CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++;
|
||||||
frame->rbp = rbp;
|
frame->rbp = vm->fiber->ret;
|
||||||
frame->closure = closure;
|
frame->closure = closure;
|
||||||
frame->ip = closure->fn->fn->opcodes.data;
|
frame->ip = closure->fn->fn->opcodes.data;
|
||||||
|
|
||||||
@ -927,6 +935,8 @@ L_vm_main_loop:
|
|||||||
|
|
||||||
ASSERT(imported->body != NULL, OOPS);
|
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
|
// 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
|
// 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
|
// 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
|
// 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
|
// rbp without modifying it. So at the end of the main function the
|
||||||
// stack top would be the module itself.
|
// 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.
|
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
|
||||||
|
CHECK_ERROR(); //< Stack overflow.
|
||||||
}
|
}
|
||||||
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
@ -1076,8 +1086,9 @@ L_do_call:
|
|||||||
(instruction == OP_SUPER_CALL), OOPS);
|
(instruction == OP_SUPER_CALL), OOPS);
|
||||||
|
|
||||||
UPDATE_FRAME(); //< Update the current frame's ip.
|
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.
|
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
|
||||||
|
CHECK_ERROR(); //< Stack overflow.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user