2021-02-07 15:40:00 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021 Thakee Nathees
|
|
|
|
* Licensed under: MIT License
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vm.h"
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
#include "core.h"
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
#define HAS_ERROR() (vm->error != NULL)
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
2021-02-07 15:40:00 +08:00
|
|
|
|
|
|
|
// Track the total allocated memory of the VM to trigger the GC.
|
2021-02-11 01:23:48 +08:00
|
|
|
// if vmRealloc is called for freeing the old_size would be 0 since
|
|
|
|
// deallocated bytes are traced by garbage collector.
|
2021-02-07 15:40:00 +08:00
|
|
|
self->bytes_allocated += new_size - old_size;
|
|
|
|
|
|
|
|
// TODO: If vm->bytes_allocated > some_value -> GC();
|
|
|
|
|
|
|
|
if (new_size == 0) {
|
|
|
|
free(memory);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return realloc(memory, new_size);
|
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
void vmInit(MSVM* self, MSConfiguration* config) {
|
|
|
|
memset(self, 0, sizeof(MSVM));
|
|
|
|
self->config = *config;
|
|
|
|
}
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
void vmPushTempRef(MSVM* self, Object* obj) {
|
2021-02-07 15:40:00 +08:00
|
|
|
ASSERT(obj != NULL, "Cannot reference to NULL.");
|
|
|
|
if (self->temp_reference_count < MAX_TEMP_REFERENCE,
|
|
|
|
"Too many temp references");
|
|
|
|
self->temp_reference[self->temp_reference_count++] = obj;
|
|
|
|
}
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
void vmPopTempRef(MSVM* self) {
|
2021-02-07 15:40:00 +08:00
|
|
|
ASSERT(self->temp_reference_count > 0, "Temporary reference is empty to pop.");
|
|
|
|
self->temp_reference_count--;
|
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
/******************************************************************************
|
|
|
|
* RUNTIME *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
// TODO: A function for quick debug. REMOVE.
|
|
|
|
void _printStackTop(MSVM* vm) {
|
|
|
|
if (vm->sp != vm->stack) {
|
|
|
|
Var v = *(vm->sp - 1);
|
|
|
|
double n = AS_NUM(v);
|
|
|
|
printf("%f\n", n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void msSetRuntimeError(MSVM* vm, const char* format, ...) {
|
|
|
|
vm->error = newString(vm, "TODO:", 5);
|
|
|
|
ASSERT(false, "TODO:");
|
|
|
|
}
|
|
|
|
|
|
|
|
void vmReportError(MSVM* vm) {
|
|
|
|
ASSERT(HAS_ERROR(), "runtimeError() should be called after an error.");
|
|
|
|
ASSERT(false, "TODO: create debug.h");
|
|
|
|
}
|
|
|
|
|
|
|
|
MSInterpretResult msInterpret(MSVM* vm, const char* file) {
|
|
|
|
Script* script = compileSource(vm, file);
|
|
|
|
// TODO: add script to vm's scripts array.
|
|
|
|
return vmRunScript(vm, script);
|
|
|
|
}
|
|
|
|
|
|
|
|
MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|
|
|
|
|
|
|
uint8_t* ip;
|
|
|
|
CallFrame* frame;
|
|
|
|
|
|
|
|
#define PUSH(value) (*vm->sp++ = (value))
|
|
|
|
#define POP() (*(--vm->sp))
|
|
|
|
#define DROP() (--vm->sp)
|
|
|
|
#define PEEK() (vm->sp - 1)
|
|
|
|
#define READ_BYTE() (*ip++)
|
|
|
|
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
|
|
|
|
|
|
|
|
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
|
|
|
|
#define CHECK_ERROR() \
|
|
|
|
do { \
|
|
|
|
if (HAS_ERROR()) { \
|
|
|
|
msRuntimeError(vm); \
|
|
|
|
return RESULT_RUNTIME_ERROR; \
|
|
|
|
} \
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
#ifdef OPCODE
|
|
|
|
#error "OPCODE" should not be deifined here.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define DEBUG_INSTRUCTION()
|
|
|
|
|
|
|
|
#define SWITCH(code) \
|
|
|
|
L_vm_main_loop: \
|
|
|
|
DEBUG_INSTRUCTION(); \
|
|
|
|
switch (code = (Opcode)READ_BYTE())
|
|
|
|
#define OPCODE(code) case OP_##code
|
|
|
|
#define DISPATCH() goto L_vm_main_loop
|
|
|
|
|
|
|
|
Script* script = _script; //< Currently executing script.
|
|
|
|
ip = script->body->fn->opcodes.data;
|
|
|
|
|
|
|
|
int stack_size = utilPowerOf2Ceil(script->body->fn->stack_size + 1);
|
|
|
|
vm->stack_size = stack_size;
|
|
|
|
vm->stack = ALLOCATE_ARRAY(vm, Var, vm->stack_size);
|
|
|
|
vm->sp = vm->stack;
|
|
|
|
|
|
|
|
vm->frames_size = 4; // TODO: Magic number.
|
|
|
|
vm->frames = ALLOCATE_ARRAY(vm, CallFrame, vm->frames_size);
|
|
|
|
frame = vm->frames;
|
|
|
|
|
|
|
|
Opcode instruction;
|
|
|
|
SWITCH(instruction) {
|
|
|
|
|
|
|
|
OPCODE(CONSTANT):
|
|
|
|
PUSH(script->literals.data[READ_SHORT()]);
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(PUSH_NULL):
|
|
|
|
PUSH(VAR_NULL);
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(PUSH_TRUE):
|
|
|
|
PUSH(VAR_TRUE);
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(PUSH_FALSE):
|
|
|
|
PUSH(VAR_FALSE);
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(PUSH_LOCAL_0):
|
|
|
|
OPCODE(PUSH_LOCAL_1):
|
|
|
|
OPCODE(PUSH_LOCAL_2):
|
|
|
|
OPCODE(PUSH_LOCAL_3):
|
|
|
|
OPCODE(PUSH_LOCAL_4):
|
|
|
|
OPCODE(PUSH_LOCAL_5):
|
|
|
|
OPCODE(PUSH_LOCAL_6):
|
|
|
|
OPCODE(PUSH_LOCAL_7):
|
|
|
|
OPCODE(PUSH_LOCAL_8):
|
|
|
|
OPCODE(PUSH_LOCAL_N):
|
|
|
|
ASSERT(false, "TODO:");
|
|
|
|
|
|
|
|
OPCODE(STORE_LOCAL_0):
|
|
|
|
OPCODE(STORE_LOCAL_1):
|
|
|
|
OPCODE(STORE_LOCAL_2):
|
|
|
|
OPCODE(STORE_LOCAL_3):
|
|
|
|
OPCODE(STORE_LOCAL_4):
|
|
|
|
OPCODE(STORE_LOCAL_5):
|
|
|
|
OPCODE(STORE_LOCAL_6):
|
|
|
|
OPCODE(STORE_LOCAL_7):
|
|
|
|
OPCODE(STORE_LOCAL_8):
|
|
|
|
OPCODE(STORE_LOCAL_N):
|
|
|
|
ASSERT(false, "TODO:");
|
|
|
|
|
|
|
|
OPCODE(PUSH_GLOBAL):
|
|
|
|
OPCODE(STORE_GLOBAL):
|
|
|
|
OPCODE(PUSH_GLOBAL_EXT):
|
|
|
|
OPCODE(STORE_GLOBAL_EXT):
|
|
|
|
OPCODE(PUSH_FN):
|
|
|
|
OPCODE(PUSH_FN_EXT):
|
|
|
|
|
|
|
|
OPCODE(POP):
|
|
|
|
DROP();
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(CALL_0):
|
|
|
|
OPCODE(CALL_1):
|
|
|
|
OPCODE(CALL_2):
|
|
|
|
OPCODE(CALL_3):
|
|
|
|
OPCODE(CALL_4):
|
|
|
|
OPCODE(CALL_5):
|
|
|
|
OPCODE(CALL_6):
|
|
|
|
OPCODE(CALL_7):
|
|
|
|
OPCODE(CALL_8):
|
|
|
|
OPCODE(CALL_N):
|
|
|
|
OPCODE(CALL_EXT_0):
|
|
|
|
OPCODE(CALL_EXT_1):
|
|
|
|
OPCODE(CALL_EXT_2):
|
|
|
|
OPCODE(CALL_EXT_3):
|
|
|
|
OPCODE(CALL_EXT_4):
|
|
|
|
OPCODE(CALL_EXT_5):
|
|
|
|
OPCODE(CALL_EXT_6):
|
|
|
|
OPCODE(CALL_EXT_7):
|
|
|
|
OPCODE(CALL_EXT_8):
|
|
|
|
OPCODE(CALL_EXT_N):
|
|
|
|
OPCODE(JUMP):
|
|
|
|
OPCODE(JUMP_NOT):
|
|
|
|
OPCODE(JUMP_IF_NOT):
|
|
|
|
OPCODE(RETURN):
|
|
|
|
OPCODE(GET_ATTRIB):
|
|
|
|
OPCODE(SET_ATTRIB):
|
|
|
|
OPCODE(GET_SUBSCRIPT):
|
|
|
|
OPCODE(SET_SUBSCRIPT):
|
|
|
|
OPCODE(NEGATIVE):
|
|
|
|
OPCODE(NOT):
|
|
|
|
OPCODE(BIT_NOT):
|
|
|
|
ASSERT(false, "TODO:");
|
|
|
|
|
|
|
|
OPCODE(ADD):
|
|
|
|
PUSH(varAdd(vm, POP(), POP()));
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(SUBTRACT):
|
|
|
|
PUSH(varSubtract(vm, POP(), POP()));
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(MULTIPLY):
|
|
|
|
PUSH(varMultiply(vm, POP(), POP()));
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(DIVIDE):
|
|
|
|
PUSH(varDivide(vm, POP(), POP()));
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(MOD):
|
|
|
|
OPCODE(BIT_AND):
|
|
|
|
OPCODE(BIT_OR):
|
|
|
|
OPCODE(BIT_XOR):
|
|
|
|
OPCODE(BIT_LSHIFT):
|
|
|
|
OPCODE(BIT_RSHIFT):
|
|
|
|
OPCODE(AND):
|
|
|
|
OPCODE(OR):
|
|
|
|
OPCODE(EQEQ):
|
|
|
|
OPCODE(NOTEQ):
|
|
|
|
OPCODE(LT):
|
|
|
|
OPCODE(LTEQ):
|
|
|
|
OPCODE(GT):
|
|
|
|
OPCODE(GTEQ):
|
|
|
|
OPCODE(RANGE):
|
|
|
|
OPCODE(IN):
|
|
|
|
OPCODE(END):
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|