some new things, mostly not working or finished
This commit is contained in:
285
niacin/cpp/vm_core.c
Normal file
285
niacin/cpp/vm_core.c
Normal file
@@ -0,0 +1,285 @@
|
||||
#include "vm_types.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
// Safe memory allocation with overflow checking
|
||||
static void* safe_malloc(size_t size) {
|
||||
if (size == 0 || size > SIZE_MAX / 2) return NULL;
|
||||
void* ptr = malloc(size);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void* safe_realloc(void* ptr, size_t size) {
|
||||
if (size == 0 || size > SIZE_MAX / 2) return NULL;
|
||||
void* new_ptr = realloc(ptr, size);
|
||||
if (!new_ptr) {
|
||||
fprintf(stderr, "Memory reallocation failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
// Initialize VM with security limits
|
||||
vm_t* vm_create(uint8_t* bytecode, size_t size) {
|
||||
vm_t* vm = safe_malloc(sizeof(vm_t));
|
||||
memset(vm, 0, sizeof(vm_t));
|
||||
|
||||
vm->bytecode = bytecode;
|
||||
vm->bytecode_size = size;
|
||||
vm->ip = 0;
|
||||
vm->halted = false;
|
||||
|
||||
// Security limits
|
||||
vm->max_stack_size = 65536;
|
||||
vm->max_call_depth = 256;
|
||||
vm->current_call_depth = 0;
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
// Safe bytecode reading with bounds checking
|
||||
static bool read_u8(vm_t* vm, uint8_t* result) {
|
||||
if (vm->ip >= vm->bytecode_size) return false;
|
||||
*result = vm->bytecode[vm->ip++];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_u16(vm_t* vm, uint16_t* result) {
|
||||
if (vm->ip + 2 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 2);
|
||||
vm->ip += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_u32(vm_t* vm, uint32_t* result) {
|
||||
if (vm->ip + 4 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 4);
|
||||
vm->ip += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_i32(vm_t* vm, int32_t* result) {
|
||||
if (vm->ip + 4 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 4);
|
||||
vm->ip += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_f32(vm_t* vm, float* result) {
|
||||
if (vm->ip + 4 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 4);
|
||||
vm->ip += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stack operations with bounds checking
|
||||
static bool stack_push(frame_t* frame, value_t value) {
|
||||
if (frame->stack_size >= frame->stack_capacity) {
|
||||
size_t new_capacity = frame->stack_capacity * 2;
|
||||
if (new_capacity == 0) new_capacity = 16;
|
||||
|
||||
value_t* new_stack = safe_realloc(frame->stack, new_capacity * sizeof(value_t));
|
||||
if (!new_stack) return false;
|
||||
|
||||
frame->stack = new_stack;
|
||||
frame->stack_capacity = new_capacity;
|
||||
}
|
||||
|
||||
frame->stack[frame->stack_size++] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool stack_pop(frame_t* frame, value_t* result) {
|
||||
if (frame->stack_size == 0) return false;
|
||||
*result = frame->stack[--frame->stack_size];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Type checking and conversion
|
||||
static bool type_check_binary(value_t a, value_t b, type_code_t* result_type) {
|
||||
// Simple type checking - in real implementation, add proper type promotion
|
||||
if (a.type == b.type) {
|
||||
*result_type = a.type;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int32_t value_to_int(value_t val) {
|
||||
switch (val.type) {
|
||||
case TYPE_I8: return val.data.i8;
|
||||
case TYPE_U8: return val.data.u8;
|
||||
case TYPE_I16: return val.data.i16;
|
||||
case TYPE_U16: return val.data.u16;
|
||||
case TYPE_I32: return val.data.i32;
|
||||
case TYPE_U32: return (int32_t)val.data.u32;
|
||||
case TYPE_BOOL: return val.data.boolean ? 1 : 0;
|
||||
case TYPE_CHAR: return (int32_t)val.data.character;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static float value_to_float(value_t val) {
|
||||
switch (val.type) {
|
||||
case TYPE_F32: return val.data.f32;
|
||||
case TYPE_I32: return (float)val.data.i32;
|
||||
case TYPE_U32: return (float)val.data.u32;
|
||||
default: return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Bytecode validation
|
||||
bool vm_validate_bytecode(vm_t* vm) {
|
||||
// Check magic number
|
||||
if (vm->bytecode_size < 4 || memcmp(vm->bytecode, "POPC", 4) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add more validation as needed
|
||||
return true;
|
||||
}
|
||||
|
||||
// Execute single instruction with full error checking
|
||||
bool vm_execute_instruction(vm_t* vm) {
|
||||
if (vm->halted || vm->ip >= vm->bytecode_size) return false;
|
||||
|
||||
uint8_t opcode;
|
||||
if (!read_u8(vm, &opcode)) return false;
|
||||
|
||||
frame_t* frame = vm->current_frame;
|
||||
if (!frame) return false;
|
||||
|
||||
switch (opcode) {
|
||||
case OP_PUSH_CONST: {
|
||||
uint32_t const_idx;
|
||||
if (!read_u32(vm, &const_idx) || const_idx >= vm->constants_count) {
|
||||
return false;
|
||||
}
|
||||
if (!stack_push(frame, vm->constants[const_idx])) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PUSH_INT: {
|
||||
uint8_t width;
|
||||
int32_t value;
|
||||
if (!read_u8(vm, &width) || !read_i32(vm, &value)) return false;
|
||||
|
||||
value_t val = { 0 };
|
||||
switch (width) {
|
||||
case 8: val.type = TYPE_I8; val.data.i8 = (int8_t)value; break;
|
||||
case 16: val.type = TYPE_I16; val.data.i16 = (int16_t)value; break;
|
||||
case 32: val.type = TYPE_I32; val.data.i32 = value; break;
|
||||
default: return false;
|
||||
}
|
||||
if (!stack_push(frame, val)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_ADD: {
|
||||
value_t a, b;
|
||||
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
|
||||
|
||||
type_code_t result_type;
|
||||
if (!type_check_binary(a, b, &result_type)) return false;
|
||||
|
||||
value_t result = { 0 };
|
||||
result.type = result_type;
|
||||
|
||||
if (result_type == TYPE_F32) {
|
||||
result.data.f32 = value_to_float(b) + value_to_float(a);
|
||||
}
|
||||
else {
|
||||
result.data.i32 = value_to_int(b) + value_to_int(a);
|
||||
}
|
||||
|
||||
if (!stack_push(frame, result)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_CMP_EQ: {
|
||||
value_t a, b;
|
||||
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
|
||||
|
||||
value_t result = { 0 };
|
||||
result.type = TYPE_BOOL;
|
||||
result.data.boolean = (value_to_int(a) == value_to_int(b));
|
||||
|
||||
if (!stack_push(frame, result)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_JMP: {
|
||||
int32_t offset;
|
||||
if (!read_i32(vm, &offset)) return false;
|
||||
|
||||
// Check for negative jumps (security)
|
||||
if (offset < 0 && (size_t)(-offset) > vm->ip) return false;
|
||||
if (vm->ip + offset >= vm->bytecode_size) return false;
|
||||
|
||||
vm->ip += offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PRINT: {
|
||||
value_t val;
|
||||
if (!stack_pop(frame, &val)) return false;
|
||||
|
||||
switch (val.type) {
|
||||
case TYPE_I32: printf("%d\n", val.data.i32); break;
|
||||
case TYPE_F32: printf("%f\n", val.data.f32); break;
|
||||
case TYPE_BOOL: printf("%s\n", val.data.boolean ? "true" : "false"); break;
|
||||
case TYPE_STR: printf("%s\n", val.data.string); break;
|
||||
default: printf("<unknown>\n"); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_HALT:
|
||||
vm->halted = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown opcode: 0x%02x\n", opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Main execution loop
|
||||
void vm_execute(vm_t* vm) {
|
||||
while (!vm->halted && vm_execute_instruction(vm)) {
|
||||
// Continue execution
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
void vm_destroy(vm_t* vm) {
|
||||
if (!vm) return;
|
||||
|
||||
// Free constants
|
||||
for (size_t i = 0; i < vm->constants_count; i++) {
|
||||
if (vm->constants[i].type == TYPE_STR && vm->constants[i].data.string) {
|
||||
free(vm->constants[i].data.string);
|
||||
}
|
||||
}
|
||||
free(vm->constants);
|
||||
|
||||
// Free functions
|
||||
free(vm->functions);
|
||||
|
||||
// Free frames (simplified - in real impl, walk call stack)
|
||||
if (vm->current_frame) {
|
||||
free(vm->current_frame->locals);
|
||||
free(vm->current_frame->stack);
|
||||
free(vm->current_frame);
|
||||
}
|
||||
|
||||
free(vm);
|
||||
}
|
||||
Reference in New Issue
Block a user