From 9ffbdcb3e92dad3f28adb7ad0bd45dc43114de3a Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 11 Feb 2017 14:01:06 -0500 Subject: [PATCH] Add simple disassembler for debugging. Does not use labels. --- .gitignore | 3 + Makefile | 4 +- compile.c | 153 ++++++++++++++++++++-------------------- compile.h | 3 + disasm.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++ disasm.h | 17 +++++ main.c | 20 ++++-- vm.c | 17 +++-- vm.h | 3 + 9 files changed, 331 insertions(+), 89 deletions(-) create mode 100644 disasm.c create mode 100644 disasm.h diff --git a/.gitignore b/.gitignore index 638ba6f1..9791f25b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Target interp +# Valgrind files +vgcore.* + # Created by https://www.gitignore.io/api/c ### C ### diff --git a/Makefile b/Makefile index aef5e261..96dede03 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ TARGET=interp PREFIX=/usr/local # C sources -HEADERS=vm.h ds.h compile.h parse.h value.h -SOURCES=main.c parse.c value.c vm.c ds.c compile.c +HEADERS=vm.h ds.h compile.h parse.h value.h disasm.h +SOURCES=main.c parse.c value.c vm.c ds.c compile.c disasm.c OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) all: $(TARGET) diff --git a/compile.c b/compile.c index 1e2a287f..5f53c4fa 100644 --- a/compile.c +++ b/compile.c @@ -79,7 +79,7 @@ struct Scope { static Slot SlotDefault() { Slot slot; slot.index = 0; - slot.isDud = 0; + slot.isDud = 1; slot.isTemp = 0; return slot; } @@ -103,9 +103,13 @@ BufferDefine(Number, Number); BufferDefine(UInt16, uint16_t); BufferDefine(Int16, int16_t); +static void onError(Compiler * c) { + ; +} + /* If there is an error during compilation, * jump back to start */ -#define CError(c, e) ((c)->error = (e), longjmp((c)->onError, 1)) +#define CError(c, e) (onError(c), (c)->error = (e), longjmp((c)->onError, 1)) /* Push a new scope in the compiler and return * a pointer to it for configuration. There is @@ -168,30 +172,34 @@ static uint16_t CompilerGetLocal(Compiler * c, Scope * scope) { return scope->nextLocal++; } else { uint16_t ret = scope->freeHeap[0]; - uint16_t left, right, current, * heap; + uint16_t * heap; uint32_t currentIndex = 0; + if (scope->heapSize == 0) { + CError(c, "Invalid freeing of slot."); + } heap = scope->freeHeap; heap[0] = heap[--scope->heapSize]; /* Min Heap Bubble down */ - while (currentIndex < scope->heapSize) { + for (;;) { uint32_t leftIndex = 2 * currentIndex + 1; uint32_t rightIndex = leftIndex + 1; - current = heap[currentIndex]; - left = heap[leftIndex]; - right = heap[rightIndex]; - if (current == left || current == right) { - CError(c, "Double slot allocation. Error in Compiler."); - } - if (left < current && left <= right) { - heap[currentIndex] = left; - heap[leftIndex] = current; - currentIndex = leftIndex; - } else if (right < current && right <= left) { - heap[currentIndex] = left; - heap[leftIndex] = current; - currentIndex = leftIndex; + uint32_t minIndex; + if (rightIndex >= scope->heapSize) { + if (leftIndex >= scope->heapSize) { + break; + } else { + minIndex = leftIndex; + } } else { - break; + minIndex = heap[leftIndex] < heap[rightIndex] ? leftIndex : rightIndex; + } + if (heap[minIndex] < heap[currentIndex]) { + uint16_t temp = heap[currentIndex]; + heap[currentIndex] = heap[minIndex]; + heap[minIndex] = temp; + currentIndex = minIndex; + } else if (heap[minIndex] == heap[currentIndex]) { + CError(c, "Double slot allocation. Error in Compiler."); } } return ret; @@ -202,7 +210,7 @@ static uint16_t CompilerGetLocal(Compiler * c, Scope * scope) { /* Free a slot on the stack for other locals and/or * intermediate values */ static void CompilerFreeLocal(Compiler * c, Scope * scope, uint16_t slot) { - if (slot == scope->nextLocal - 1) { + if (slot == scope->nextLocal - 1) { --scope->nextLocal; return; } else { @@ -249,7 +257,7 @@ static void CompilerTrackerInit(Compiler * c, SlotTracker * tracker) { * belong to a named local). If the slot does belong * to a named variable, does nothing. */ static void CompilerDropSlot(Compiler * c, Scope * scope, Slot slot) { - if (slot.isTemp && !(slot.isTemp)) { + if (!slot.isDud && slot.isTemp) { CompilerFreeLocal(c, scope, slot.index); } } @@ -363,9 +371,9 @@ static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) { Slot ret = SlotDefault(); uint16_t literalIndex; if (opts.canDrop) { - ret.isDud = 1; return ret; } + ret.isDud = 0; if (opts.canChoose) { ret.isTemp = 1; ret.index = CompilerGetLocal(c, scope); @@ -387,14 +395,14 @@ static Slot CompileNonReferenceType(Compiler * c, FormOptions opts, Value x) { /* If the value is not used, the compiler can just immediately * ignore it as there are no side effects. */ if (opts.canDrop) { - ret.isDud = 1; return ret; } + ret.isDud = 0; if (opts.canChoose) { ret.index = CompilerGetLocal(c, scope); ret.isTemp = 1; } else { - ret.index = (uint16_t) opts.target; + ret.index = opts.target; } if (x.type == TYPE_NIL) { BufferPushUInt16(c->vm, buffer, VM_OP_NIL); @@ -437,9 +445,9 @@ static Slot CompileSymbol(Compiler * c, FormOptions opts, Value sym) { /* We can just do nothing if we are dropping the * results, as dereferencing a symbol has no side effects. */ if (opts.canDrop) { - ret.isDud = 1; return ret; } + ret.isDud = 0; if (ScopeSymbolResolve(scope, sym, &level, &index)) { if (level > 0) { /* We have an upvalue */ @@ -499,6 +507,7 @@ static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) { } } if (!opts.canDrop) { + ret.isDud = 0; /* Write Dictionary literal opcode */ if (opts.canChoose) { ret.isTemp = 1; @@ -508,9 +517,7 @@ static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) { } BufferPushUInt16(c->vm, buffer, VM_OP_DIC); BufferPushUInt16(c->vm, buffer, ret.index); - BufferPushUInt16(c->vm, buffer, dict->count); - } else { - ret.isDud = 1; + BufferPushUInt16(c->vm, buffer, dict->count * 2); } /* Write the location of all of the arguments */ CompilerTrackerFree(c, scope, &tracker, 1); @@ -538,6 +545,7 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) { CompilerTrackerPush(c, &tracker, slot); } if (!opts.canDrop) { + ret.isDud = 0; /* Write Array literal opcode */ if (opts.canChoose) { ret.isTemp = 1; @@ -548,8 +556,6 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) { BufferPushUInt16(c->vm, buffer, VM_OP_ARR); BufferPushUInt16(c->vm, buffer, ret.index); BufferPushUInt16(c->vm, buffer, array->count); - } else { - ret.isDud = 1; } /* Write the location of all of the arguments */ CompilerTrackerFree(c, scope, &tracker, 1); @@ -575,41 +581,35 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form, FormOptions subOpts = FormOptionsDefault(); SlotTracker tracker; uint32_t i; - /* Calculate sub options */ - subOpts.canDrop = opts.canDrop; /* Compile all of the arguments */ CompilerTrackerInit(c, &tracker); for (i = 1; i < form->count; ++i) { Slot slot = CompileValue(c, subOpts, form->data[i]); - if (subOpts.canDrop) - CompilerDropSlot(c, scope, slot); - else - CompilerTrackerPush(c, &tracker, slot); + CompilerTrackerPush(c, &tracker, slot); } - if (!opts.canDrop) { - /* Write the correct opcode */ - if (form->count < 2) { - if (op0 < 0) CError(c, "This operator does not take 0 arguments."); - BufferPushUInt16(c->vm, buffer, op0); - } else if (form->count == 2) { - if (op1 < 0) CError(c, "This operator does not take 1 argument."); - BufferPushUInt16(c->vm, buffer, op1); - } else if (form->count == 3) { - if (op2 < 0) CError(c, "This operator does not take 2 arguments."); - BufferPushUInt16(c->vm, buffer, op2); - } else { - if (opn < 0) CError(c, "This operator does not take n arguments."); - BufferPushUInt16(c->vm, buffer, opn); - BufferPushUInt16(c->vm, buffer, form->count - 1); - } - if (opts.canChoose) { - ret.isTemp = 1; - ret.index = CompilerGetLocal(c, scope); - } else { - ret.isDud = opts.target; - } - BufferPushUInt16(c->vm, buffer, ret.index); + ret.isDud = 0; + /* Write the correct opcode */ + if (form->count < 2) { + if (op0 < 0) CError(c, "This operator does not take 0 arguments."); + BufferPushUInt16(c->vm, buffer, op0); + } else if (form->count == 2) { + if (op1 < 0) CError(c, "This operator does not take 1 argument."); + BufferPushUInt16(c->vm, buffer, op1); + } else if (form->count == 3) { + if (op2 < 0) CError(c, "This operator does not take 2 arguments."); + BufferPushUInt16(c->vm, buffer, op2); + } else { + if (opn < 0) CError(c, "This operator does not take n arguments."); + BufferPushUInt16(c->vm, buffer, opn); + BufferPushUInt16(c->vm, buffer, form->count - 1); } + if (opts.canChoose) { + ret.isTemp = 1; + ret.index = CompilerGetLocal(c, scope); + } else { + ret.index = opts.target; + } + BufferPushUInt16(c->vm, buffer, ret.index); /* Write the location of all of the arguments */ CompilerTrackerFree(c, scope, &tracker, reverseOperands ? 2 : 1); return ret; @@ -650,10 +650,9 @@ static Slot CompileNot(Compiler * c, FormOptions opts, Array * form) { /* Helper function to return nil from a form that doesn't do anything (set, while, etc.) */ static Slot CompileReturnNil(Compiler * c, FormOptions opts) { - Slot ret; + Slot ret = SlotDefault(); /* If we need a return value, we just use nil */ if (opts.isTail) { - ret.isDud = 1; BufferPushUInt16(c->vm, c->buffer, VM_OP_RTN); } else if (!opts.canDrop) { ret.isDud = 0; @@ -726,16 +725,15 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t uint32_t current = startIndex; /* Compile the body */ while (current < form->count) { - subOpts.canDrop = (current != form->count - 1) || opts.canDrop; + subOpts.canDrop = (current != form->count - 1); subOpts.isTail = opts.isTail && !subOpts.canDrop; ret = CompileValue(c, subOpts, form->data[current]); if (subOpts.canDrop) { CompilerDropSlot(c, scope, ret); - ret.isDud = 1; } ++current; } - if (opts.isTail && !ret.isDud) { + if (opts.isTail) { CompilerReturnSlot(c, ret); ret.isDud = 1; } @@ -791,9 +789,9 @@ static Slot CompileFunction(Compiler * c, FormOptions opts, Array * form) { /* Do nothing if we can drop. This is rather pointless from * a language point of view, but it doesn't hurt. */ if (opts.canDrop) { - ret.isDud = 1; return ret; } + ret.isDud = 0; subScope = CompilerPushScope(c, 0); /* Check for function documentation - for now just ignore. */ if (form->data[current].type == TYPE_STRING) { @@ -862,11 +860,10 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) { /* Configure options for bodies of if */ if (opts.isTail) { ret.isDud = 1; - resOpts.isTail = 1; } else if (opts.canDrop) { - ret.isDud = 1; resOpts.canDrop = 1; } else if (opts.canChoose) { + ret.isDud = 0; ret.isTemp = 1; ret.index = CompilerGetLocal(c, scope); resOpts.target = ret.index; @@ -896,10 +893,7 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) { BufferPushUInt32(c->vm, buffer, 0); /* Compile true path */ left = CompileValue(c, resOpts, form->data[2]); - if (opts.isTail && !left.isDud) { - BufferPushUInt16(c->vm, buffer, VM_OP_RET); - BufferPushUInt16(c->vm, buffer, left.index); - } + if (opts.isTail) CompilerReturnSlot(c, left); CompilerDropSlot(c, scope, left); /* If we need to jump again, do so */ if (!opts.isTail && form->count == 4) { @@ -917,10 +911,7 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) { /* Compile false path */ if (form->count == 4) { right = CompileValue(c, resOpts, form->data[3]); - if (opts.isTail && !right.isDud) { - BufferPushUInt16(c->vm, buffer, VM_OP_RET); - BufferPushUInt16(c->vm, buffer, right.index); - } + if (opts.isTail) CompilerReturnSlot(c, right); CompilerDropSlot(c, scope, right); } /* Set the jump length */ @@ -992,9 +983,9 @@ static Slot CompileQuote(Compiler * c, FormOptions opts, Array * form) { return CompileNonReferenceType(c, opts, x); } if (opts.canDrop) { - ret.isDud = 1; return ret; } + ret.isDud = 0; if (opts.canChoose) { ret.isTemp = 1; ret.index = CompilerGetLocal(c, scope); @@ -1158,9 +1149,9 @@ static Slot CompileForm(Compiler * c, FormOptions opts, Array * form) { } /* If this is in tail position do a tail call. */ if (opts.isTail) { - ret.isDud = 1; BufferPushUInt16(c->vm, buffer, VM_OP_TCL); } else { + ret.isDud = 0; BufferPushUInt16(c->vm, buffer, VM_OP_CAL); if (opts.canDrop) { ret.isTemp = 1; @@ -1216,6 +1207,14 @@ void CompilerAddGlobal(Compiler * c, const char * name, Value x) { ArrayPush(c->vm, c->env, x); } +/* Register a global c function for the compilation environment. */ +void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f) { + Value func; + func.type = TYPE_CFUNCTION; + func.data.cfunction = f; + return CompilerAddGlobal(c, name, func); +} + /* Compile interface. Returns a function that evaluates the * given AST. Returns NULL if there was an error during compilation. */ Func * CompilerCompile(Compiler * c, Value form) { diff --git a/compile.h b/compile.h index 033f159d..3206eb6e 100644 --- a/compile.h +++ b/compile.h @@ -9,6 +9,9 @@ void CompilerInit(Compiler * c, VM * vm); /* Register a global for the compilation environment. */ void CompilerAddGlobal(Compiler * c, const char * name, Value x); +/* Register a global c function for the compilation environment. */ +void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f); + /* Compile a function that evaluates the given form. */ Func * CompilerCompile(Compiler * c, Value form); diff --git a/disasm.c b/disasm.c new file mode 100644 index 00000000..839bbdcf --- /dev/null +++ b/disasm.c @@ -0,0 +1,200 @@ +#include "disasm.h" + +/* Width of padded opcode names */ +#define OP_WIDTH 20 + +/* Print various register and arguments to instructions */ +static void dasmPrintSlot(FILE * out, uint16_t index) { fprintf(out, "%d ", index); } +static void dasmPrintI16(FILE * out, int16_t number) { fprintf(out, "#%d ", number); } +static void dasmPrintI32(FILE * out, int32_t number) { fprintf(out, "#%d ", number); } +static void dasmPrintF64(FILE * out, double number) { fprintf(out, "#%f ", number); } +static void dasmPrintLiteral(FILE * out, uint16_t index) { fprintf(out, "(%d) ", index); } +static void dasmPrintUpValue(FILE * out, uint16_t level, uint16_t index) { + fprintf(out, "<%d, %d> ", level, index); +} + +/* Print the name of the argument but pad it */ +static void dasmPrintArg(FILE * out, const char * name) { + uint32_t i = 0; + char c; + while ((c = *name++)) { + fputc(c, out); + ++i; + } + for (; i < OP_WIDTH; ++i) + fputc(' ', out); +} + +/* Print instructions that take a fixed number of arguments */ +static uint32_t dasmPrintFixedOp(FILE * out, const uint16_t * current, + const char * name, uint32_t size) { + uint32_t i; + dasmPrintArg(out, name); + for (i = 1; i <= size; ++i) { + dasmPrintSlot(out, current[i]); + } + return size + 1; +} + +/* Print instructions that take a variable number of arguments */ +static uint32_t dasmPrintVarArgOp(FILE * out, const uint16_t * current, + const char * name, uint32_t extra) { + uint32_t i, argCount; + dasmPrintArg(out, name); + argCount = current[1]; + for (i = 0; i < extra; ++i) { + dasmPrintSlot(out, current[i + 2]); + } + fprintf(out, "| "); /* Argument separator */ + for (i = 0; i < argCount; ++i) { + dasmPrintSlot(out, current[i + extra + 2]); + } + return argCount + extra + 2; +} + +/* Print the disassembly for a function definition */ +void dasmFuncDef(FILE * out, FuncDef * def) { + dasm(out, def->byteCode, def->byteCodeLen); +} + +/* Print the disassembly for a function */ +void dasmFunc(FILE * out, Func * f) { + dasm(out, f->def->byteCode, f->def->byteCodeLen); +} + +/* Disassemble some bytecode and display it as opcode + arguments assembly */ +void dasm(FILE * out, uint16_t *byteCode, uint32_t len) { + uint16_t *current = byteCode; + uint16_t *end = byteCode + len; + + while (current < end) { + switch (*current) { + case VM_OP_ADD: + current += dasmPrintFixedOp(out, current, "add", 3); + break; + case VM_OP_SUB: + current += dasmPrintFixedOp(out, current, "sub", 3); + break; + case VM_OP_MUL: + current += dasmPrintFixedOp(out, current, "mul", 3); + break; + case VM_OP_DIV: + current += dasmPrintFixedOp(out, current, "div", 3); + break; + case VM_OP_NOT: + current += dasmPrintFixedOp(out, current, "not", 2); + break; + case VM_OP_LD0: + current += dasmPrintFixedOp(out, current, "load0", 1); + break; + case VM_OP_LD1: + current += dasmPrintFixedOp(out, current, "load1", 1); + break; + case VM_OP_FLS: + current += dasmPrintFixedOp(out, current, "loadFalse", 1); + break; + case VM_OP_TRU: + current += dasmPrintFixedOp(out, current, "loadTrue", 1); + break; + case VM_OP_NIL: + current += dasmPrintFixedOp(out, current, "loadNil", 1); + break; + case VM_OP_I16: + dasmPrintArg(out, "loadInt16"); + dasmPrintSlot(out, current[1]); + dasmPrintI16(out, ((int16_t *)current)[2]); + current += 3; + break; + case VM_OP_UPV: + dasmPrintArg(out, "loadUpValue"); + dasmPrintSlot(out, current[1]); + dasmPrintUpValue(out, current[2], current[3]); + current += 4; + break; + case VM_OP_JIF: + dasmPrintArg(out, "jumpIf"); + dasmPrintSlot(out, current[1]); + dasmPrintI32(out, ((int32_t *)(current + 2))[0]); + current += 4; + break; + case VM_OP_JMP: + dasmPrintArg(out, "jump"); + dasmPrintI32(out, ((int32_t *)(current + 1))[0]); + current += 3; + break; + case VM_OP_CAL: + current += dasmPrintVarArgOp(out, current, "call", 2); + break; + case VM_OP_RET: + current += dasmPrintFixedOp(out, current, "return", 1); + break; + case VM_OP_SUV: + dasmPrintArg(out, "setUpValue"); + dasmPrintSlot(out, current[1]); + dasmPrintUpValue(out, current[2], current[3]); + current += 4; + break; + case VM_OP_CST: + dasmPrintArg(out, "loadLiteral"); + dasmPrintSlot(out, current[1]); + dasmPrintLiteral(out, current[2]); + current += 3; + break; + case VM_OP_I32: + dasmPrintArg(out, "loadInt32"); + dasmPrintSlot(out, current[1]); + dasmPrintI32(out, ((int32_t *)(current + 2))[0]); + current += 4; + break; + case VM_OP_F64: + dasmPrintArg(out, "loadFloat64"); + dasmPrintSlot(out, current[1]); + dasmPrintF64(out, ((double *)(current + 2))[0]); + current += 6; + break; + case VM_OP_MOV: + current += dasmPrintFixedOp(out, current, "move", 2); + break; + case VM_OP_CLN: + dasmPrintArg(out, "makeClosure"); + dasmPrintSlot(out, current[1]); + dasmPrintLiteral(out, current[2]); + current += 3; + break; + case VM_OP_EQL: + current += dasmPrintFixedOp(out, current, "equals", 3); + break; + case VM_OP_LTN: + current += dasmPrintFixedOp(out, current, "lessThan", 3); + break; + case VM_OP_LTE: + current += dasmPrintFixedOp(out, current, "lessThanEquals", 3); + break; + case VM_OP_ARR: + current += dasmPrintVarArgOp(out, current, "array", 1); + break; + case VM_OP_DIC: + current += dasmPrintVarArgOp(out, current, "dictionary", 1); + break; + case VM_OP_TCL: + current += dasmPrintVarArgOp(out, current, "tailCall", 1); + break; + case VM_OP_ADM: + current += dasmPrintVarArgOp(out, current, "addMultiple", 1); + break; + case VM_OP_SBM: + current += dasmPrintVarArgOp(out, current, "subMultiple", 1); + break; + case VM_OP_MUM: + current += dasmPrintVarArgOp(out, current, "mulMultiple", 1); + break; + case VM_OP_DVM: + current += dasmPrintVarArgOp(out, current, "divMultiple", 1); + break; + case VM_OP_RTN: + current += dasmPrintFixedOp(out, current, "returnNil", 0); + break; + } + fprintf(out, "\n"); + } +} diff --git a/disasm.h b/disasm.h new file mode 100644 index 00000000..4e56ff13 --- /dev/null +++ b/disasm.h @@ -0,0 +1,17 @@ +#ifndef disasm_h_INCLUDED +#define disasm_h_INCLUDED + +#include "datatypes.h" +#include + +/* Print disassembly for a given funciton */ +void dasm(FILE * out, uint16_t * byteCode, uint32_t len); + +/* Print the disassembly for a function definition */ +void dasmFuncDef(FILE * out, FuncDef * def); + +/* Print the disassembly for a function */ +void dasmFunc(FILE * out, Func * f); + +#endif // disasm_h_INCLUDED + diff --git a/main.c b/main.c index 68a77903..947d1eab 100644 --- a/main.c +++ b/main.c @@ -6,15 +6,19 @@ #include "parse.h" #include "compile.h" #include "value.h" +#include "disasm.h" Value print(VM * vm) { - uint32_t i; + uint32_t i, j, count; Value nil; - uint8_t * string = ValueToString(vm, VMGetArg(vm, 0)); - uint32_t len = VStringSize(string); - for (i = 0; i < len; ++i) - fputc(string[i], stdout); - fputc('\n', stdout); + count = VMCountArgs(vm); + for (j = 0; j < count; ++j) { + uint8_t * string = ValueToString(vm, VMGetArg(vm, j)); + uint32_t len = VStringSize(string); + for (i = 0; i < len; ++i) + fputc(string[i], stdout); + fputc('\n', stdout); + } nil.type = TYPE_NIL; return nil; } @@ -69,6 +73,7 @@ void debugRepl() { /* Try to compile generated AST */ CompilerInit(&c, &vm); + CompilerAddGlobalCFunc(&c, "print", print); func = CompilerCompile(&c, p.value); /* Check for compilation errors */ @@ -79,6 +84,9 @@ void debugRepl() { continue; } + /* Print the function that will be executed */ + dasmFunc(stdout, func); + /* Execute function */ VMLoad(&vm, func); if (VMStart(&vm)) { diff --git a/vm.c b/vm.c index a615875a..eda94c2b 100644 --- a/vm.c +++ b/vm.c @@ -9,7 +9,7 @@ #define VMOpArg(i) (VMArg(vm->pc[(i)])) static const char OOM[] = "Out of memory"; -static const char NO_UPVALUE[] = "Out of memory"; +static const char NO_UPVALUE[] = "No upvalue"; static const char EXPECTED_FUNCTION[] = "Expected function"; static const char VMS_EXPECTED_NUMBER_ROP[] = "Expected right operand to be number"; static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be number"; @@ -272,7 +272,10 @@ static void VMThreadPop(VM * vm, Array * thread) { static Value * GetUpValue(VM * vm, Func * fn, uint16_t level, uint16_t index) { FuncEnv * env; Value * stack; - while (fn && level--) + if (!level) { + return vm->base + index; + } + while (fn && --level) fn = fn->parent; VMAssert(vm, fn, NO_UPVALUE); env = fn->env; @@ -452,6 +455,7 @@ int VMStart(VM * vm) { { int n; if ((n = setjmp(vm->jump))) { + vm->lock = 0; /* Good return */ if (n == 1) { return 0; @@ -569,7 +573,7 @@ int VMStart(VM * vm) { vRet = VMOpArg(1); *vRet = *GetUpValue(vm, callee.data.func, vm->pc[2], vm->pc[3]); vm->pc += 4; - } + } break; case VM_OP_JIF: /* Jump If */ @@ -679,7 +683,7 @@ int VMStart(VM * vm) { uint32_t i = 3; uint32_t kvs = vm->pc[2]; Dictionary * dict = DictNew(vm, kvs); - kvs = kvs * 2 + 3; + kvs = kvs + 3; while (i < kvs) { v1 = VMOpArg(i++); v2 = VMOpArg(i++); @@ -759,6 +763,11 @@ void VMSetArg(VM * vm, uint16_t index, Value x) { *VMArg(index) = x; } +/* Get the size of the VMStack */ +uint16_t VMCountArgs(VM * vm) { + return FrameSize(vm->thread); +} + #undef VMOpArg #undef VMArg diff --git a/vm.h b/vm.h index 0aa8ebaf..e047e7d8 100644 --- a/vm.h +++ b/vm.h @@ -50,4 +50,7 @@ Value VMGetArg(VM * vm, uint16_t index); /* Put a value on the stack */ void VMSetArg(VM * vm, uint16_t index, Value x); +/* Get the number of arguments on the stack */ +uint16_t VMCountArgs(VM * vm); + #endif /* end of include guard: VM_H_C4OZU8CQ */