278 lines
6.3 KiB
C
278 lines
6.3 KiB
C
#include <common/fzl_builtins.h>
|
|
#include <common/fzl_opcodes.h>
|
|
#include <common/fzl_types.h>
|
|
#include <common/fzl_value.h>
|
|
#include <compiler/utils/fzl_const_pool.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <vm/fzl_vm.h>
|
|
#include <vm/fzl_vm_env.h>
|
|
#include <vm/fzl_vm_object.h>
|
|
#include <vm/internals/opcode.h>
|
|
|
|
char last_err[256];
|
|
size_t last_jmp_ip = 0;
|
|
size_t total_jmp_on_same = 0;
|
|
|
|
static char *expect_hdr = "FZL\0\1";
|
|
|
|
fzl_vm_t *fzl_mkvm(const char *filepath)
|
|
{
|
|
fzl_vm_t *vm = malloc(sizeof(fzl_vm_t));
|
|
if (!vm)
|
|
{
|
|
snprintf(last_err, sizeof(last_err), "failed to allocate vm");
|
|
return NULL;
|
|
}
|
|
|
|
memset(vm, 0, sizeof(fzl_vm_t));
|
|
|
|
FILE *fd = fopen(filepath, "rb");
|
|
if (!fd)
|
|
{
|
|
snprintf(last_err, sizeof(last_err), "failed to open file '%s'", filepath);
|
|
free(vm);
|
|
return NULL;
|
|
}
|
|
|
|
char hdr[5];
|
|
fread(hdr, sizeof(char), 5,
|
|
fd); // NOTE: add red bytes check (also for freads below)
|
|
if (memcmp(hdr, expect_hdr, 5) != 0)
|
|
{
|
|
snprintf(last_err, sizeof(last_err), "magic check error");
|
|
fclose(fd);
|
|
free(vm);
|
|
return NULL;
|
|
}
|
|
|
|
size_t consts_count, ops_count;
|
|
fread(&consts_count, sizeof(size_t), 1, fd);
|
|
fread(&ops_count, sizeof(size_t), 1, fd);
|
|
// TODO: read externs count
|
|
|
|
if (ops_count < 1)
|
|
{
|
|
snprintf(last_err, sizeof(last_err), "there is nothing to do (0 ops)");
|
|
fclose(fd);
|
|
free(vm);
|
|
return NULL;
|
|
}
|
|
|
|
if (consts_count > 0)
|
|
{
|
|
vm->consts_count = consts_count;
|
|
vm->consts = malloc(consts_count * sizeof(fzl_value_t));
|
|
|
|
for (size_t i = 0; i < consts_count; i++)
|
|
{
|
|
fzl_consttype_t type;
|
|
fread(&type, sizeof(fzl_consttype_t), 1, fd);
|
|
|
|
if (type == FZL_CONSTTYPE_STRING)
|
|
{
|
|
size_t length;
|
|
char *chars;
|
|
|
|
fread(&length, sizeof(size_t), 1, fd);
|
|
|
|
chars = malloc(length * sizeof(char) + 1);
|
|
fread(chars, sizeof(char), length, fd);
|
|
chars[length] = 0;
|
|
|
|
vm->consts[i] = (fzl_const_t){
|
|
.type = type, .as.string.length = length, .as.string.chars = chars};
|
|
}
|
|
else
|
|
{
|
|
fclose(fd);
|
|
free(vm->consts);
|
|
free(vm);
|
|
snprintf(last_err, sizeof(last_err), "unknown const type at index %ld",
|
|
i);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
vm->ops_count = ops_count;
|
|
vm->ops = malloc(ops_count * sizeof(uint64_t));
|
|
for (size_t i = 0; i < ops_count; i++)
|
|
{
|
|
uint64_t op;
|
|
fread(&op, sizeof(uint64_t), 1, fd);
|
|
vm->ops[i] = op;
|
|
}
|
|
|
|
// TODO: read externs
|
|
fclose(fd);
|
|
|
|
return vm;
|
|
}
|
|
|
|
void print_object(fzl_vm_object_t *obj)
|
|
{
|
|
if (!obj)
|
|
{
|
|
printf("<null>\n");
|
|
return;
|
|
}
|
|
|
|
printf("obj<");
|
|
for (size_t i = 0; i < obj->values_count; i++)
|
|
{
|
|
fzl_value_t arg = obj->values[i];
|
|
|
|
if (i > 0)
|
|
printf(", ");
|
|
|
|
if (arg.type == FZL_TYPE_STRING)
|
|
printf("str(%s)", arg.as.s.value);
|
|
else if (arg.type == FZL_TYPE_CHAR)
|
|
printf("char(%c)", arg.as.c);
|
|
else if (arg.type == FZL_TYPE_INT)
|
|
printf("int(%ld)", arg.as.i);
|
|
else if (arg.type == FZL_TYPE_UINT)
|
|
printf("uint(%lu)", arg.as.u);
|
|
else if (arg.type == FZL_TYPE_BOOL)
|
|
printf("bool(%s)", arg.as.b ? "true" : "false");
|
|
else if (arg.type == FZL_TYPE_NULL)
|
|
printf("<null>");
|
|
else if (arg.type == FZL_TYPE_OBJECT)
|
|
print_object(arg.as.obj);
|
|
else
|
|
printf("<unknown>");
|
|
}
|
|
|
|
printf(">");
|
|
}
|
|
|
|
void print_err_data(fzl_vm_t *vm)
|
|
{
|
|
printf("===== ERROR =====\n");
|
|
printf("> Caused on IP: %zu\n", vm->ip);
|
|
|
|
printf("\n> Data stack:\n");
|
|
if (vm->dtop > 0)
|
|
{
|
|
for (ssize_t i = vm->dtop - 1; i >= 0; i--)
|
|
{
|
|
printf("\t> [%d] ", vm->dstack[i].type);
|
|
switch (vm->dstack[i].type)
|
|
{
|
|
case FZL_TYPE_OBJECT:
|
|
printf("ref: %p\n", vm->dstack[i].as.obj);
|
|
break;
|
|
case FZL_TYPE_STRING:
|
|
printf("str: '%s'\n", vm->dstack[i].as.s.value);
|
|
break;
|
|
case FZL_TYPE_NULL:
|
|
printf("<null>\n");
|
|
break;
|
|
default:
|
|
printf("%lu\n", vm->dstack[i].as.u);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("\tEmpty\n");
|
|
}
|
|
|
|
printf("\n> Environments:\n");
|
|
for (ssize_t i = vm->etop - 1; i >= 0; i--)
|
|
{
|
|
fzl_vm_env_t *env = vm->estack[i];
|
|
printf("\t> [%zd] %p:\n", i, env);
|
|
|
|
printf("\t\t> Owned objects (%zu/%zu):\n", env->owned_objects_count,
|
|
env->owned_objects_remainder);
|
|
for (size_t j = 0; j < env->owned_objects_count; j++)
|
|
printf("\t\t\t> ref: %p (entries: %zu)\n", env->owned_objects[j],
|
|
env->owned_objects[j]->values_count);
|
|
|
|
printf("\t\t> Local entries (%zu/%zu):\n", env->size, env->remainder);
|
|
for (size_t j = 0; j < env->size; j++)
|
|
{
|
|
switch (env->table[j].value.type)
|
|
{
|
|
case FZL_TYPE_OBJECT:
|
|
printf("\t\t\t> [%d] %s = ref %p\n", env->table[j].value.type,
|
|
env->table[j].name, env->table[j].value.as.obj);
|
|
break;
|
|
|
|
default:
|
|
printf("\t\t\t> [%d] %s = %lu\n", env->table[j].value.type,
|
|
env->table[j].name, env->table[j].value.as.u);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("\n> Call stack:\n");
|
|
for (ssize_t i = vm->ctop - 1; i >= 0; i--)
|
|
printf("\t> [%zd] %lu\n", i, vm->cstack[i]);
|
|
}
|
|
|
|
int fzl_vm_run(fzl_vm_t *vm)
|
|
{
|
|
if (!vm)
|
|
{
|
|
snprintf(last_err, sizeof(last_err), "vm is null");
|
|
return -1;
|
|
}
|
|
|
|
vm->ip = 0;
|
|
int result = 0;
|
|
while (vm->ip < vm->ops_count)
|
|
{
|
|
fzl_opcode_t opcode = vm->ops[vm->ip];
|
|
size_t op_group = (opcode >> 16) & 0xFF;
|
|
size_t op_fnnum = (opcode & 0xFF);
|
|
|
|
if (master_tbl[op_group] == NULL || master_tbl[op_group][op_fnnum] == NULL)
|
|
{
|
|
snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %u",
|
|
opcode);
|
|
print_err_data(vm);
|
|
return -1;
|
|
}
|
|
|
|
opcode_handler handler = master_tbl[op_group][op_fnnum];
|
|
result = handler(vm);
|
|
|
|
if (result != 0)
|
|
return result;
|
|
|
|
vm->ip++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *fzl_vm_geterror() { return last_err; }
|
|
|
|
void fzl_freevm(fzl_vm_t *vm)
|
|
{
|
|
if (!vm)
|
|
return;
|
|
|
|
while (vm->etop > 0)
|
|
fzl_freeenv(vm->estack[--vm->etop]);
|
|
|
|
for (size_t i = 0; i < vm->consts_count; i++)
|
|
{
|
|
if (vm->consts[i].type == FZL_CONSTTYPE_STRING)
|
|
free((void *)vm->consts[i].as.string.chars);
|
|
}
|
|
|
|
free(vm->consts);
|
|
free(vm->ops);
|
|
free(vm);
|
|
}
|