| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /**
- ******************************************************************************
- * @file : vm.c
- * @author : simon
- * @brief : Interpret the bytecode from chunk
- * @attention : None
- * @date : 2023/8/17
- ******************************************************************************
- */
- #include "vm.h"
- #include "common.h"
- #include "compiler.h"
- #include "debug.h"
- #include "memory.h"
- #include <stdarg.h>
- #include <time.h>
- VM vm;
- /// \brief 重置栈
- static void resetStack() {
- vm.stackTop = vm.stack;// 指针指向栈底
- vm.frameCount = 0;
- vm.openUpvalues = NULL;
- }
- static void runtimeError(const char *format, ...) {
- va_list args;
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- fputs("\n", stderr);
- for (int i = vm.frameCount - 1; i >= 0; i--) {
- CallFrame *frame = &vm.frames[i];
- ObjFunction *function = frame->closure->function;
- size_t instruction = frame->ip - function->chunk.code - 1;
- int line = function->chunk.lines[instruction];
- fprintf(stderr, "[line %d] in ", line);
- if (function->name == NULL) {
- fprintf(stderr, "script\n");
- } else {
- fprintf(stderr, "%s()\n", function->name->chars);
- }
- }
- resetStack();
- }
- static void defineNative(const char *name, NativeFn function) {
- push(OBJ_VAL(copyString(name, (int) strlen(name))));
- push(OBJ_VAL(newNative(function)));
- tableSet(&vm.globals, AS_STRING(vm.stack[0]), vm.stack[1]);
- pop();
- pop();
- }
- static Value clockNative(int argCount, Value *args) {
- return NUMBER_VAL((double) clock() / CLOCKS_PER_SEC);
- }
- void initVM() {
- resetStack();
- vm.objects = NULL;
- vm.bytesAllocated = 0;
- vm.nextGC = 1024 * 1024;
- vm.grayCount = 0;
- vm.grayCapacity = 0;
- vm.grayStack = NULL;
- initTable(&vm.globals);
- initTable(&vm.strings);
- /// Native Function define.
- defineNative("clock", clockNative);
- }
- static Value peek(int distance) {
- return vm.stackTop[-1 - distance];
- }
- static bool call(ObjClosure *closure, int argCount) {
- if (argCount != closure->function->arity) {
- runtimeError("Expected %d arguments but got %d", closure->function->arity, argCount);
- return false;
- }
- if (vm.frameCount == FRAMES_MAX) {
- runtimeError("Stack overflow.");
- return false;
- }
- CallFrame *frame = &vm.frames[vm.frameCount++];
- frame->closure = closure;
- frame->ip = closure->function->chunk.code;
- frame->slots = vm.stackTop - argCount - 1;
- return true;
- }
- static bool callValue(Value callee, int argCount) {
- if (IS_OBJ(callee)) {
- switch (OBJ_TYPE(callee)) {
- case OBJ_BOUND_METHOD: {
- ObjBoundMethod *bound = AS_BOUND_METHOD(callee);
- vm.stackTop[-argCount-1] = bound->receiver;
- return call(bound->method, argCount);
- }
- case OBJ_CLOSURE:
- return call(AS_CLOSURE(callee), argCount);
- case OBJ_NATIVE: {
- NativeFn fn = AS_NATIVE(callee);
- Value result = fn(argCount, vm.stackTop - argCount);
- vm.stackTop -= argCount + 1;
- push(result);
- return true;
- }
- case OBJ_CLASS: {
- ObjClass *klass = AS_CLASS(callee);
- vm.stackTop[-argCount - 1] = OBJ_VAL(newInstance(klass));
- return true;
- }
- default:
- break;// Non-callable object type.
- }
- }
- runtimeError("Can only call functions and classes.");
- return false;
- }
- static bool isFalse(Value value) {
- // the nil and false will return true
- return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
- }
- static void concatenate() {
- ObjString *b = AS_STRING(peek(0));
- ObjString *a = AS_STRING(peek(1));
- int length = a->length + b->length;
- char *chars = ALLOCATE(char, length + 1);
- memcpy(chars, a->chars, a->length);
- memcpy(chars + a->length, b->chars, b->length);
- chars[length] = '\0';
- ObjString *result = takeString(chars, length);
- pop();
- pop();
- push(OBJ_VAL(result));
- }
- static ObjUpvalue *captureUpvalue(Value *local) {
- ObjUpvalue *preUpvalue = NULL;
- ObjUpvalue *upvalue = vm.openUpvalues;
- while (upvalue != NULL && upvalue->location > local) {
- preUpvalue = upvalue;
- upvalue = upvalue->next;
- }
- if (upvalue != NULL && upvalue->location == local) {
- return upvalue;
- }
- ObjUpvalue *createdUpvalue = newUpvalue(local);
- if (preUpvalue == NULL) {
- vm.openUpvalues = createdUpvalue;
- } else {
- preUpvalue->next = createdUpvalue;
- }
- return createdUpvalue;
- }
- static void closeUpvalues(Value *last) {
- while (vm.openUpvalues != NULL && vm.openUpvalues->location >= last) {
- ObjUpvalue *upvalue = vm.openUpvalues;
- upvalue->closed = *upvalue->location;
- upvalue->location = &upvalue->closed;
- vm.openUpvalues = upvalue->next;
- }
- }
- static void defineMethod(ObjString *name) {
- Value method = peek(0);
- ObjClass *klass = AS_CLASS(peek(1));
- tableSet(&klass->methods, name, method);
- pop();
- }
- static bool bindKlassMethod(ObjClass *klass, ObjString *name) {
- Value method;
- if (!tableGet(&klass->methods, name, &method)) {
- runtimeError("Undefined property '%s'.", name->chars);
- return false;
- }
- ObjBoundMethod *bound = newBoundMethod(peek(0), AS_CLOSURE(method));
- pop();
- push(OBJ_VAL(bound));
- return true;
- }
- /// VM run function - exec opcode
- /// \return
- static InterpretResult run() {
- CallFrame *frame = &vm.frames[vm.frameCount - 1];
- //! <macro> reads the byte currently pointed at by ip
- //! and then advances the instruction pointer
- #define READ_BYTE() (*frame->ip++)
- //! reads the next byte from the bytecode
- //! treats the resulting number as an index
- #define READ_CONSTANT() \
- (frame->closure->function->chunk.constants.values[READ_BYTE()])
- #define READ_SHORT() \
- (frame->ip += 2, (uint16_t) ((frame->ip[-2] << 8) | frame->ip[-1]))
- #define READ_STRING() AS_STRING(READ_CONSTANT())
- #define BINARY_OP(valueType, op) \
- do { \
- if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
- runtimeError("Operands must be numbers."); \
- return INTERPRET_RUNTIME_ERROR; \
- } \
- double b = AS_NUMBER(pop()); \
- double a = AS_NUMBER(pop()); \
- push(valueType(a op b)); \
- } while (false)
- for (;;) {
- #ifdef DEBUG_TRACE_EXECUTION
- #ifdef DEBUG_STACK_INFO
- //! <Stack tracing> start
- printf("\n!!! <Stack tracing now>:\n");
- printf("------------------------\n|");
- for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
- printf("[ ");
- printValue(*slot);
- printf(" ]");
- }
- printf("\n------------------------");
- printf("\n");
- printf("\n");
- //! <Stack tracing> end
- #endif
- printf("The Instruction: \n");
- disassembleInstruction(&frame->closure->function->chunk, (int) (frame->ip - frame->closure->function->chunk.code));
- #endif
- uint8_t opCode = READ_BYTE();
- switch (opCode) {
- case OP_CONSTANT: {
- Value constant = READ_CONSTANT();
- push(constant);
- break;
- }
- case OP_NIL:
- push(NIL_VAL);
- break;
- case OP_TRUE:
- push(BOOL_VAL(true));
- break;
- case OP_FALSE:
- push(BOOL_VAL(false));
- break;
- case OP_POP:
- pop();
- break;
- case OP_DEFINE_GLOBAL: {
- ObjString *name = READ_STRING();
- tableSet(&vm.globals, name, peek(0));
- pop();
- break;
- }
- case OP_CLASS:
- push(OBJ_VAL(newClass(READ_STRING())));
- break;
- case OP_GET_GLOBAL: {
- ObjString *name = READ_STRING();
- Value value;
- if (!tableGet(&vm.globals, name, &value)) {
- runtimeError("Undefined variable '%s'.", name->chars);
- return INTERPRET_RUNTIME_ERROR;
- }
- push(value);
- break;
- }
- case OP_METHOD: {
- defineMethod(READ_STRING());
- break;
- }
- case OP_SET_GLOBAL: {
- ObjString *name = READ_STRING();
- if (tableSet(&vm.globals, name, peek(0))) {
- tableDelete(&vm.globals, name);
- runtimeError("Undefined variable '%s'.", name->chars);
- return INTERPRET_RUNTIME_ERROR;
- }
- break;
- }
- case OP_GET_LOCAL: {
- uint8_t slot = READ_BYTE();
- push(frame->slots[slot]);
- break;
- }
- case OP_SET_LOCAL: {
- uint8_t slot = READ_BYTE();
- frame->slots[slot] = peek(0);
- break;
- }
- case OP_ADD: {
- if (IS_STRING(peek(0)) && IS_STRING(peek(1))) {
- // 字串拼接
- concatenate();
- } else if (IS_NUMBER(peek(0)) && IS_NUMBER(peek(1))) {
- double b = AS_NUMBER(pop());
- double a = AS_NUMBER(pop());
- push(NUMBER_VAL(a + b));
- } else {
- runtimeError("Operands must be two numbers or two strings.");
- return INTERPRET_RUNTIME_ERROR;
- }
- break;
- }
- case OP_SUBTRACT:
- BINARY_OP(NUMBER_VAL, -);
- break;
- case OP_MULTIPLY:
- BINARY_OP(NUMBER_VAL, *);
- break;
- case OP_DIVIDE:
- BINARY_OP(NUMBER_VAL, /);
- break;
- case OP_NOT:
- push(BOOL_VAL(isFalse(pop())));
- break;
- case OP_NEGATE:
- if (!IS_NUMBER(peek(0))) {
- runtimeError("Operand must be a number.");
- return INTERPRET_RUNTIME_ERROR;
- }
- push(NUMBER_VAL(-AS_NUMBER(pop())));
- break;
- case OP_EQUAL: {
- Value b = pop();
- Value a = pop();
- push(BOOL_VAL(valuesEqual(a, b)));
- break;
- }
- case OP_GREATER:
- BINARY_OP(BOOL_VAL, >);
- break;
- case OP_LESS:
- BINARY_OP(BOOL_VAL, <);
- break;
- case OP_PRINT: {
- Value value = pop();
- printf("****PRINT**** ");
- printValue(value);
- printf("\n");
- break;
- }
- case OP_JUMP_IF_FALSE: {
- uint16_t offset = READ_SHORT();
- if (isFalse(peek(0))) frame->ip += offset;
- break;
- }
- case OP_JUMP: {
- uint16_t offset = READ_SHORT();
- frame->ip += offset;
- break;
- }
- case OP_LOOP: {
- uint16_t offset = READ_SHORT();
- frame->ip -= offset;
- break;
- }
- case OP_CALL: {
- int argCount = READ_BYTE();
- if (!callValue(peek(argCount), argCount)) {
- return INTERPRET_RUNTIME_ERROR;
- }
- frame = &vm.frames[vm.frameCount - 1];
- break;
- }
- case OP_GET_PROPERTY: {
- if (!IS_INSTANCE(peek(0))) {
- runtimeError("Only instance have properties.");
- return INTERPRET_RUNTIME_ERROR;
- }
- ObjInstance *instance = AS_INSTANCE(peek(0));
- ObjString *name = READ_STRING();
- Value value;
- if (tableGet(&instance->fields, name, &value)) {
- pop();// Instance
- push(value);
- break;
- }
- if (!bindKlassMethod(instance->klass, name)) {
- return INTERPRET_RUNTIME_ERROR;
- }
- break;
- }
- case OP_SET_PROPERTY: {
- if (!IS_INSTANCE(peek(1))) {
- runtimeError("Only instances have fields.");
- return INTERPRET_RUNTIME_ERROR;
- }
- ObjInstance *instance = AS_INSTANCE(peek(1));
- tableSet(&instance->fields, READ_STRING(), peek(0));
- Value value = pop();
- pop();
- push(value);
- break;
- }
- case OP_CLOSURE: {
- ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
- ObjClosure *closure = newClosure(function);
- push(OBJ_VAL(closure));
- for (int i = 0; i < closure->upvalueCount; i++) {
- uint8_t isLocal = READ_BYTE();
- uint8_t index = READ_BYTE();
- if (isLocal) {
- closure->upvalues[i] = captureUpvalue(frame->slots + index);
- } else {
- closure->upvalues[i] = frame->closure->upvalues[index];
- }
- }
- break;
- }
- case OP_GET_UPVALUE: {
- uint8_t slot = READ_BYTE();
- push(*frame->closure->upvalues[slot]->location);
- break;
- }
- case OP_SET_UPVALUE: {
- uint8_t slot = READ_BYTE();
- *frame->closure->upvalues[slot]->location = peek(0);
- break;
- }
- case OP_CLOSE_UPVALUE:
- closeUpvalues(vm.stackTop - 1);
- pop();
- break;
- case OP_RETURN: {
- Value result = pop();
- closeUpvalues(frame->slots);
- vm.frameCount--;
- if (vm.frameCount == 0) {
- pop();
- return INTERPRET_OK;
- }
- vm.stackTop = frame->slots;
- push(result);
- frame = &vm.frames[vm.frameCount - 1];
- break;
- }
- default:
- break;
- }
- }
- #undef READ_BYTE
- #undef READ_CONSTANT
- #undef READ_SHORT
- #undef READ_STRING
- #undef BINARY_OP
- }
- void freeVM() {
- freeTable(&vm.globals);
- freeTable(&vm.strings);
- freeObjects();
- }
- InterpretResult interpret(const char *source) {
- ObjFunction *function = compile(source);
- if (function == NULL) return INTERPRET_COMPILE_ERROR;
- push(OBJ_VAL(function));
- ObjClosure *closure = newClosure(function);
- pop();
- push(OBJ_VAL(closure));
- // CallFrame *frame = &vm.frames[vm.frameCount++];
- // frame->function = function;
- // frame->ip = function->chunk.code;
- // frame->slots = vm.stack;
- call(closure, 0);
- return run();
- }
- void push(Value value) {
- *vm.stackTop = value;
- vm.stackTop++;
- #ifdef DEBUG_TRACE_EXECUTION
- printf(">> push stack: ");
- printValue(value);
- printf("\n");
- #endif
- }
- Value pop() {
- vm.stackTop--;
- //! NOTE: 出栈后,里面的值没有清除
- Value value = *vm.stackTop;
- #ifdef DEBUG_TRACE_EXECUTION
- printf("<< pop stack: ");
- printValue(value);
- printf("\n");
- #endif
- return value;
- }
|