<commit_before>#include "vm.h"
#include "codegen.h"
#include "memory.h"
#include "error.h"
#include "stdint.h"
#include "math.h"
#include "global.h"
#include "native.h"
#include "imports.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>

// Forward Declarations
static struct data eval_binop(struct vm * vm, enum operator op, struct data a, struct data b);
static struct data eval_uniop(struct vm * vm, enum operator op, struct data a);
static struct data type_of(struct data a);
static struct data size_of(struct data a);
static struct data value_of(struct data a);
static struct data char_of(struct data a);

struct vm *vm_init() {
	struct vm* vm = safe_malloc(sizeof(*vm));
	vm->bytecode = 0;
	vm->instruction_ptr = 0;
	vm->bytecode_size = 0;
	vm->last_pushed_identifier = 0;
	vm->line = 0;
	vm->memory = memory_init();
	return vm;
}

void vm_destroy(struct vm * vm) {
	memory_destroy(vm->memory);
	safe_free(vm);
}

void vm_cleanup_if_repl(struct vm * vm) {
	// bytecode could be null if codegen didn't generate anything
	if (vm->bytecode) safe_free(vm->bytecode);
}

#define num_args(...) (sizeof((char*[]){__VA_ARGS__})/sizeof(char*))
#define first_that(memory, condition, ...) first_that_impl(memory, condition, num_args(__VA_ARGS__), __VA_ARGS__)
static char* first_that_impl(struct memory* memory, bool (*condition)(struct memory *, char*), int count, ...) {
	va_list ap;
	va_start(ap, count);
	for (int i = 0; i < count; i++) {
		char* s = va_arg(ap, char*);
		if (condition(memory, s)) {
			return s;
		}
	}
	va_end(ap);
	return 0;
}

static bool _id_exist(struct memory* memory, char* fn) {
	return id_exist(memory, fn, true);
}

static char* get_binary_overload_name(struct vm* vm, enum operator op, struct data a, struct data b) {
	struct data type_a = type_of(a);
	struct data type_b = type_of(b);
	char* fn_name = safe_concat(OPERATOR_OVERLOAD_PREFIX, type_a.value.string,
		operator_string[op], type_b.value.string);

	destroy_data_runtime(vm->memory, &type_a);
	destroy_data_runtime(vm->memory, &type_b);
	return fn_name;
}

static char* get_unary_overload_name(struct vm* vm, enum operator op, struct data a) {
	struct data type_a = type_of(a);
	char* fn_name = safe_concat(OPERATOR_OVERLOAD_PREFIX,
		operator_string[op], type_a.value.string);

	destroy_data_runtime(vm->memory, &type_a);
	return fn_name;
}

static char* get_print_overload_name(struct vm* vm, struct data a) {
	struct data type_a = type_of(a);
	char* fn_name = safe_concat(OPERATOR_OVERLOAD_PREFIX "@", type_a.value.string);
	destroy_data_runtime(vm->memory, &type_a);
	return fn_name;
}

void vm_run(struct vm *vm, uint8_t* new_bytecode, size_t size) {
	if (get_settings_flag(SETTINGS_DRY_RUN)) {
		return;
	}
	// Verify Header
	address start_at;
	size_t saved_size = vm->bytecode_size;
	if (!get_settings_flag(SETTINGS_REPL)) {
		vm->bytecode = new_bytecode;
		start_at = verify_header(vm->bytecode, size);
	}
	else {
		// REPL Bytecode has no headers!
		// Resize Bytecode Block, Offset New Addresses, Push to End
		vm->bytecode_size += size;
		if (vm->bytecode) {
			// This gets rid of the OP_HALT from the previous chain of BC
			vm->bytecode_size--;
			vm->bytecode = safe_realloc(vm->bytecode, vm->bytecode_size * sizeof(uint8_t));
		}
		else {
			vm->bytecode = safe_malloc(vm->bytecode_size * sizeof(uint8_t));
		}
		if (saved_size != 0) {
			start_at = saved_size - 1;
		}
		else {
			start_at = 0;
		}
		offset_addresses(new_bytecode, size, start_at);
		for (size_t i = 0; i < size; i++) {
			vm->bytecode[start_at + i] = new_bytecode[i];
		}
	}
	#define VM_LABEL(x) &&VM_##x,
	static void* dispatch_table[] = {
		FOREACH_OPCODE(VM_LABEL)
	};
	#define DISPATCH() goto *dispatch_table[vm->bytecode[(vm->instruction_ptr)++]];
	for (vm->instruction_ptr = start_at;;) {
		reset_error_flag();
		enum opcode op = vm->bytecode[vm->instruction_ptr];
		if (get_settings_flag(SETTINGS_TRACE_VM)) {
			// This branch could slow down the VM but the CPU should branch
			// predict after one or two iterations.
			printf(BLU "<+%04X>: " RESET "%s\n", vm->instruction_ptr, opcode_string[op]);
		}
		vm->instruction_ptr += 1;
		switch (op) {
			case OP_PUSH:
			VM_OP_PUSH: {
				struct data t = get_data(vm->bytecode + vm->instruction_ptr, &vm->instruction_ptr);
				// t will never be a reference type
				struct data d;
				if (t.type == D_IDENTIFIER) {
					if (streq(t.value.string, "time")) {
						d = time_data();
					}
					else {
						struct data* value = get_value_of_id(vm->memory, t.value.string, vm->line);
						if (!value) {
							break;
						}
						d = copy_data(*value);
					}
				}
				else {
					d = copy_data(t);
				}
				vm->last_pushed_identifier = t.value.string;
				push_arg(vm->memory, d);
				DISPATCH();
				break;
			}
			case OP_BIN: 
			VM_OP_BIN: {
				enum operator op = vm->bytecode[vm->instruction_ptr++];
				struct data a = pop_arg(vm->memory, vm->line);
				struct data b = pop_arg(vm->memory, vm->line);
				struct data any_d = any_data();
				char* a_and_b = get_binary_overload_name(vm, op, a, b);
				char* any_a = get_binary_overload_name(vm, op, any_d, b);
				char* any_b = get_binary_overload_name(vm, op, a, any_d);
				destroy_data_runtime(vm->memory, &any_d);
				char* fn_name = first_that(vm->memory, _id_exist, a_and_b, any_a, any_b);
				if (fn_name) {
					push_arg(vm->memory, make_data(D_END_OF_ARGUMENTS, data_value_num(0)));
					push_arg(vm->memory, b);
					push_arg(vm->memory, a);
					push_arg(vm->memory, copy_data(*get_value_of_id(vm->memory, fn_name, vm->line)));
					safe_free(a_and_b);
					safe_free(any_a);
					safe_free(any_b);
					goto wendy_vm_call;
				}
				else {
					push_arg(vm->memory, eval_binop(vm, op, a, b));
					destroy_data_runtime(vm->memory, &a);
					destroy_data_runtime(vm->memory, &b);
				}
				safe_free(a_and_b);
				safe_free(any_a);
				safe_free(any_b);
				DISPATCH();
				break;<commit_msg>wendy: fix crash from not checking error flag using dispatched goto<commit_after>#include "vm.h"
#include "codegen.h"
#include "memory.h"
#include "error.h"
#include "stdint.h"
#include "math.h"
#include "global.h"
#include "native.h"
#include "imports.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>

// Forward Declarations
static struct data eval_binop(struct vm * vm, enum operator op, struct data a, struct data b);
static struct data eval_uniop(struct vm * vm, enum operator op, struct data a);
static struct data type_of(struct data a);
static struct data size_of(struct data a);
static struct data value_of(struct data a);
static struct data char_of(struct data a);

struct vm *vm_init() {
	struct vm* vm = safe_malloc(sizeof(*vm));
	vm->bytecode = 0;
	vm->instruction_ptr = 0;
	vm->bytecode_size = 0;
	vm->last_pushed_identifier = 0;
	vm->line = 0;
	vm->memory = memory_init();
	return vm;
}

void vm_destroy(struct vm * vm) {
	memory_destroy(vm->memory);
	safe_free(vm);
}

void vm_cleanup_if_repl(struct vm * vm) {
	// bytecode could be null if codegen didn't generate anything
	if (vm->bytecode) safe_free(vm->bytecode);
}

#define num_args(...) (sizeof((char*[]){__VA_ARGS__})/sizeof(char*))
#define first_that(memory, condition, ...) first_that_impl(memory, condition, num_args(__VA_ARGS__), __VA_ARGS__)
static char* first_that_impl(struct memory* memory, bool (*condition)(struct memory *, char*), int count, ...) {
	va_list ap;
	va_start(ap, count);
	for (int i = 0; i < count; i++) {
		char* s = va_arg(ap, char*);
		if (condition(memory, s)) {
			return s;
		}
	}
	va_end(ap);
	return 0;
}

static bool _id_exist(struct memory* memory, char* fn) {
	return id_exist(memory, fn, true);
}

static char* get_binary_overload_name(struct vm* vm, enum operator op, struct data a, struct data b) {
	struct data type_a = type_of(a);
	struct data type_b = type_of(b);
	char* fn_name = safe_concat(OPERATOR_OVERLOAD_PREFIX, type_a.value.string,
		operator_string[op], type_b.value.string);

	destroy_data_runtime(vm->memory, &type_a);
	destroy_data_runtime(vm->memory, &type_b);
	return fn_name;
}

static char* get_unary_overload_name(struct vm* vm, enum operator op, struct data a) {
	struct data type_a = type_of(a);
	char* fn_name = safe_concat(OPERATOR_OVERLOAD_PREFIX,
		operator_string[op], type_a.value.string);

	destroy_data_runtime(vm->memory, &type_a);
	return fn_name;
}

static char* get_print_overload_name(struct vm* vm, struct data a) {
	struct data type_a = type_of(a);
	char* fn_name = safe_concat(OPERATOR_OVERLOAD_PREFIX "@", type_a.value.string);
	destroy_data_runtime(vm->memory, &type_a);
	return fn_name;
}

void vm_run(struct vm *vm, uint8_t* new_bytecode, size_t size) {
	if (get_settings_flag(SETTINGS_DRY_RUN)) {
		return;
	}
	// Verify Header
	address start_at;
	size_t saved_size = vm->bytecode_size;
	if (!get_settings_flag(SETTINGS_REPL)) {
		vm->bytecode = new_bytecode;
		start_at = verify_header(vm->bytecode, size);
	}
	else {
		// REPL Bytecode has no headers!
		// Resize Bytecode Block, Offset New Addresses, Push to End
		vm->bytecode_size += size;
		if (vm->bytecode) {
			// This gets rid of the OP_HALT from the previous chain of BC
			vm->bytecode_size--;
			vm->bytecode = safe_realloc(vm->bytecode, vm->bytecode_size * sizeof(uint8_t));
		}
		else {
			vm->bytecode = safe_malloc(vm->bytecode_size * sizeof(uint8_t));
		}
		if (saved_size != 0) {
			start_at = saved_size - 1;
		}
		else {
			start_at = 0;
		}
		offset_addresses(new_bytecode, size, start_at);
		for (size_t i = 0; i < size; i++) {
			vm->bytecode[start_at + i] = new_bytecode[i];
		}
	}
	#define VM_LABEL(x) &&VM_##x,
	static void* dispatch_table[] = {
		FOREACH_OPCODE(VM_LABEL)
	};
	#define DISPATCH() { \
		if (get_error_flag()) { \
			clear_working_stack(vm->memory); \
			break; \
		} \
		goto *dispatch_table[vm->bytecode[(vm->instruction_ptr)++]]; \
	}

	for (vm->instruction_ptr = start_at;;) {
		reset_error_flag();
		enum opcode op = vm->bytecode[vm->instruction_ptr];
		if (get_settings_flag(SETTINGS_TRACE_VM)) {
			// This branch could slow down the VM but the CPU should branch
			// predict after one or two iterations.
			printf(BLU "<+%04X>: " RESET "%s\n", vm->instruction_ptr, opcode_string[op]);
		}
		vm->instruction_ptr += 1;
		switch (op) {
			case OP_PUSH:
			VM_OP_PUSH: {
				struct data t = get_data(vm->bytecode + vm->instruction_ptr, &vm->instruction_ptr);
				// t will never be a reference type
				struct data d;
				if (t.type == D_IDENTIFIER) {
					if (streq(t.value.string, "time")) {
						d = time_data();
					}
					else {
						struct data* value = get_value_of_id(vm->memory, t.value.string, vm->line);
						if (!value) {
							break;
						}
						d = copy_data(*value);
					}
				}
				else {
					d = copy_data(t);
				}
				vm->last_pushed_identifier = t.value.string;
				push_arg(vm->memory, d);
				DISPATCH();
				break;
			}
			case OP_BIN:
			VM_OP_BIN: {
				enum operator op = vm->bytecode[vm->instruction_ptr++];
				struct data a = pop_arg(vm->memory, vm->line);
				struct data b = pop_arg(vm->memory, vm->line);
				struct data any_d = any_data();
				char* a_and_b = get_binary_overload_name(vm, op, a, b);
				char* any_a = get_binary_overload_name(vm, op, any_d, b);
				char* any_b = get_binary_overload_name(vm, op, a, any_d);
				destroy_data_runtime(vm->memory, &any_d);
				char* fn_name = first_that(vm->memory, _id_exist, a_and_b, any_a, any_b);
				if (fn_name) {
					push_arg(vm->memory, make_data(D_END_OF_ARGUMENTS, data_value_num(0)));
					push_arg(vm->memory, b);
					push_arg(vm->memory, a);
					push_arg(vm->memory, copy_data(*get_value_of_id(vm->memory, fn_name, vm->line)));
					safe_free(a_and_b);
					safe_free(any_a);
					safe_free(any_b);
					goto wendy_vm_call;
				}
				else {
					push_arg(vm->memory, eval_binop(vm, op, a, b));
					destroy_data_runtime(vm->memory, &a);
					destroy_data_runtime(vm->memory, &b);
				}
				safe_free(a_and_b);
				safe_free(any_a);
				safe_free(any_b);
				DISPATCH();
				break;
{'fraction_non_alphanumeric': 0.14273838224291296, 'fraction_numerical': 0.002992767478593399, 'mean_word_length': 4.048258497691985, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 128, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '151786', 'n_tokens_mistral': 5098, 'n_tokens_neox': 4544, 'n_words': 1057}