4909 lines
172 KiB
C
Raw Normal View History

2025-12-02 06:54:32 +01:00
#define _POSIX_C_SOURCE 200809L
#include "runtime.h"
#include "labeltable.h"
#include "methodcache.h"
#include "nanbox.h"
#include "fastframe.h"
#include "superinst.h"
2025-12-04 06:14:22 +01:00
#include "../utils/safe_alloc.h"
2025-12-02 06:54:32 +01:00
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
2025-12-02 21:12:50 +01:00
#include <math.h>
#include <ctype.h>
2025-12-02 06:54:32 +01:00
2025-12-03 17:55:37 +01:00
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif
2025-12-02 06:54:32 +01:00
#define STACK_ENSURE_CAPACITY(stack) do { \
if ((stack)->top >= (stack)->capacity) { \
(stack)->capacity *= 2; \
2025-12-04 06:14:22 +01:00
RavaValue_t *_new_values = realloc((stack)->values, sizeof(RavaValue_t) * (stack)->capacity); \
if (_new_values) (stack)->values = _new_values; \
2025-12-02 06:54:32 +01:00
} \
} while(0)
#define STACK_PUSH(stack, val) do { \
STACK_ENSURE_CAPACITY(stack); \
(stack)->values[(stack)->top++] = (val); \
} while(0)
#define STACK_POP(stack) ((stack)->top > 0 ? (stack)->values[--(stack)->top] : (RavaValue_t){0})
#define STACK_PEEK(stack) ((stack)->top > 0 ? (stack)->values[(stack)->top - 1] : (RavaValue_t){0})
#define STACK_PUSH_INT(stack, v) do { \
STACK_ENSURE_CAPACITY(stack); \
RavaValue_t _v; _v.type = RAVA_VAL_INT; _v.data.int_val = (v); \
(stack)->values[(stack)->top++] = _v; \
} while(0)
#define STACK_PUSH_LONG(stack, v) do { \
STACK_ENSURE_CAPACITY(stack); \
RavaValue_t _v; _v.type = RAVA_VAL_LONG; _v.data.long_val = (v); \
(stack)->values[(stack)->top++] = _v; \
} while(0)
#define STACK_PUSH_BOOL(stack, v) do { \
STACK_ENSURE_CAPACITY(stack); \
RavaValue_t _v; _v.type = RAVA_VAL_BOOLEAN; _v.data.bool_val = (v); \
(stack)->values[(stack)->top++] = _v; \
} while(0)
#define VALUE_IS_INT(v) ((v).type == RAVA_VAL_INT)
#define VALUE_IS_LONG(v) ((v).type == RAVA_VAL_LONG)
#define VALUE_AS_INT_FAST(v) ((v).data.int_val)
#define VALUE_AS_LONG_FAST(v) ((v).data.long_val)
RavaValue_t rava_value_string(const char *str) {
RavaValue_t val;
val.type = RAVA_VAL_STRING;
val.data.string_val = str ? strdup(str) : NULL;
return val;
}
const char* rava_value_as_string(RavaValue_t value) {
if (value.type == RAVA_VAL_STRING) {
return value.data.string_val ? value.data.string_val : "";
}
return "";
}
static inline RavaNanboxValue_t rava_value_to_nanbox(RavaValue_t val) {
switch (val.type) {
case RAVA_VAL_INT: return rava_nanbox_int(val.data.int_val);
case RAVA_VAL_LONG: return rava_nanbox_long(val.data.long_val);
case RAVA_VAL_DOUBLE: return rava_nanbox_double(val.data.double_val);
case RAVA_VAL_FLOAT: return rava_nanbox_double((double)val.data.float_val);
case RAVA_VAL_BOOLEAN: return rava_nanbox_bool(val.data.bool_val);
case RAVA_VAL_NULL: return rava_nanbox_null();
case RAVA_VAL_STRING: return rava_nanbox_string(val.data.string_val);
case RAVA_VAL_OBJECT: return rava_nanbox_object(val.data.object_val);
case RAVA_VAL_ARRAY: return rava_nanbox_array(val.data.array_val);
default: return rava_nanbox_null();
}
}
static inline RavaValue_t rava_nanbox_to_value(RavaNanboxValue_t nb) {
RavaValue_t val;
if (rava_nanbox_is_int(nb)) {
val.type = RAVA_VAL_INT;
val.data.int_val = rava_nanbox_as_int(nb);
} else if (rava_nanbox_is_long(nb)) {
val.type = RAVA_VAL_LONG;
val.data.long_val = rava_nanbox_as_long(nb);
} else if (rava_nanbox_is_double(nb)) {
val.type = RAVA_VAL_DOUBLE;
val.data.double_val = rava_nanbox_as_double(nb);
} else if (rava_nanbox_is_bool(nb)) {
val.type = RAVA_VAL_BOOLEAN;
val.data.bool_val = rava_nanbox_as_bool(nb);
} else if (rava_nanbox_is_null(nb)) {
val.type = RAVA_VAL_NULL;
val.data.object_val = NULL;
} else if (rava_nanbox_is_string(nb)) {
val.type = RAVA_VAL_STRING;
val.data.string_val = (char*)rava_nanbox_as_string(nb);
} else if (rava_nanbox_is_object(nb)) {
val.type = RAVA_VAL_OBJECT;
val.data.object_val = rava_nanbox_as_object(nb);
} else if (rava_nanbox_is_array(nb)) {
val.type = RAVA_VAL_ARRAY;
val.data.array_val = rava_nanbox_as_array(nb);
} else {
val.type = RAVA_VAL_NULL;
val.data.object_val = NULL;
}
return val;
}
2025-12-03 17:55:37 +01:00
static char* _rava_get_string_buffer(RavaVM_t *vm, size_t needed) {
if (needed >= RAVA_STRING_BUFFER_SIZE) {
return malloc(needed);
}
char *buf = vm->string_pool.buffers[vm->string_pool.current];
vm->string_pool.current = (vm->string_pool.current + 1) % RAVA_STRING_BUFFER_COUNT;
return buf;
}
static uint32_t _intern_hash(const char *str) {
uint32_t hash = 5381;
while (*str) hash = ((hash << 5) + hash) ^ (uint32_t)*str++;
2025-12-04 06:14:22 +01:00
return hash;
2025-12-03 17:55:37 +01:00
}
static const char* _rava_intern_string(RavaVM_t *vm, const char *str) {
uint32_t h = _intern_hash(str);
for (size_t i = 0; i < RAVA_INTERN_TABLE_SIZE; i++) {
size_t idx = (h + i) & (RAVA_INTERN_TABLE_SIZE - 1);
if (vm->intern_table.strings[idx] == NULL) {
2025-12-04 06:14:22 +01:00
if (vm->intern_table.count >= RAVA_INTERN_TABLE_SIZE - 1) {
static bool warned = false;
if (!warned) {
fprintf(stderr, "Warning: String intern table full (%d entries)\n",
RAVA_INTERN_TABLE_SIZE);
warned = true;
}
}
2025-12-03 17:55:37 +01:00
vm->intern_table.strings[idx] = strdup(str);
vm->intern_table.count++;
return vm->intern_table.strings[idx];
}
if (strcmp(vm->intern_table.strings[idx], str) == 0) {
return vm->intern_table.strings[idx];
}
}
return strdup(str);
}
2025-12-03 15:01:02 +01:00
static inline uint32_t _rava_field_hash(const char *name) {
uint32_t hash = 5381;
while (*name) {
hash = ((hash << 5) + hash) ^ (uint32_t)*name++;
}
2025-12-04 06:14:22 +01:00
return hash;
2025-12-02 06:54:32 +01:00
}
2025-12-03 17:55:37 +01:00
static void _rava_object_set_field_vm(RavaObject_t *obj, const char *name, RavaValue_t value, RavaVM_t *vm) {
2025-12-02 06:54:32 +01:00
if (!obj || !name) return;
2025-12-04 06:14:22 +01:00
uint32_t hash = _rava_field_hash(name);
for (size_t probe = 0; probe < RAVA_OBJECT_HASH_SIZE; probe++) {
uint32_t idx = (hash + probe) & (RAVA_OBJECT_HASH_SIZE - 1);
int field_idx = obj->field_hash[idx];
if (field_idx == -1) {
if (obj->field_count >= obj->field_capacity) {
size_t new_cap = obj->field_capacity * 2;
char **new_names = rava_safe_realloc(obj->field_names, new_cap * sizeof(char*));
RavaValue_t *new_values = rava_safe_realloc(obj->field_values, new_cap * sizeof(RavaValue_t));
if (!new_names || !new_values) return;
obj->field_names = new_names;
obj->field_values = new_values;
obj->field_capacity = new_cap;
}
obj->field_names[obj->field_count] = (char*)(vm ? _rava_intern_string(vm, name) : strdup(name));
obj->field_values[obj->field_count] = value;
obj->field_hash[idx] = (int)obj->field_count;
obj->field_count++;
2025-12-02 06:54:32 +01:00
return;
}
2025-12-03 17:55:37 +01:00
2025-12-04 06:14:22 +01:00
if ((size_t)field_idx < obj->field_count &&
strcmp(obj->field_names[field_idx], name) == 0) {
obj->field_values[field_idx] = value;
return;
2025-12-02 06:54:32 +01:00
}
}
}
int32_t rava_value_as_int(RavaValue_t value) {
switch (value.type) {
case RAVA_VAL_INT: return value.data.int_val;
case RAVA_VAL_LONG: return (int32_t)value.data.long_val;
case RAVA_VAL_FLOAT: return (int32_t)value.data.float_val;
case RAVA_VAL_DOUBLE: return (int32_t)value.data.double_val;
case RAVA_VAL_BOOLEAN: return value.data.bool_val ? 1 : 0;
case RAVA_VAL_CHAR: return value.data.char_val;
default: return 0;
}
}
int64_t rava_value_as_long(RavaValue_t value) {
switch (value.type) {
case RAVA_VAL_INT: return value.data.int_val;
case RAVA_VAL_LONG: return value.data.long_val;
case RAVA_VAL_FLOAT: return (int64_t)value.data.float_val;
case RAVA_VAL_DOUBLE: return (int64_t)value.data.double_val;
default: return 0;
}
}
double rava_value_as_double(RavaValue_t value) {
switch (value.type) {
case RAVA_VAL_INT: return value.data.int_val;
case RAVA_VAL_LONG: return (double)value.data.long_val;
case RAVA_VAL_FLOAT: return value.data.float_val;
case RAVA_VAL_DOUBLE: return value.data.double_val;
default: return 0.0;
}
}
bool rava_value_as_boolean(RavaValue_t value) {
if (value.type == RAVA_VAL_BOOLEAN) {
return value.data.bool_val;
}
return rava_value_as_int(value) != 0;
}
RavaStack_t* rava_stack_create(size_t capacity) {
RavaStack_t *stack = malloc(sizeof(RavaStack_t));
2025-12-04 06:14:22 +01:00
if (!stack) return NULL;
2025-12-02 06:54:32 +01:00
stack->capacity = capacity;
stack->values = malloc(sizeof(RavaValue_t) * capacity);
2025-12-04 06:14:22 +01:00
if (!stack->values) {
free(stack);
return NULL;
}
2025-12-02 06:54:32 +01:00
stack->top = 0;
return stack;
}
void rava_stack_destroy(RavaStack_t *stack) {
if (!stack) return;
free(stack->values);
free(stack);
}
void rava_stack_push(RavaStack_t *stack, RavaValue_t value) {
if (stack->top >= stack->capacity) {
stack->capacity *= 2;
2025-12-04 06:14:22 +01:00
RavaValue_t *new_values = rava_safe_realloc(stack->values, sizeof(RavaValue_t) * stack->capacity);
if (!new_values) return;
stack->values = new_values;
2025-12-02 06:54:32 +01:00
}
stack->values[stack->top++] = value;
}
RavaValue_t rava_stack_pop(RavaStack_t *stack) {
if (stack->top == 0) {
return rava_value_int(0);
}
return stack->values[--stack->top];
}
RavaValue_t rava_stack_peek(RavaStack_t *stack) {
if (stack->top == 0) {
return rava_value_int(0);
}
return stack->values[stack->top - 1];
}
bool rava_stack_is_empty(RavaStack_t *stack) {
return stack->top == 0;
}
RavaCallFrame_t* rava_call_frame_create(RavaMethod_t *method) {
RavaCallFrame_t *frame = calloc(1, sizeof(RavaCallFrame_t));
2025-12-04 06:14:22 +01:00
if (!frame) return NULL;
2025-12-02 06:54:32 +01:00
frame->method = method;
frame->local_count = method->local_count;
frame->locals = calloc(method->local_count, sizeof(RavaValue_t));
2025-12-04 06:14:22 +01:00
if (!frame->locals && method->local_count > 0) {
free(frame);
return NULL;
}
2025-12-02 06:54:32 +01:00
frame->pc = 0;
2025-12-03 17:55:37 +01:00
size_t stack_size = method->max_stack > 0 ? (size_t)method->max_stack : 64;
frame->operand_stack = rava_stack_create(stack_size);
2025-12-04 06:14:22 +01:00
if (!frame->operand_stack) {
free(frame->locals);
free(frame);
return NULL;
}
2025-12-02 06:54:32 +01:00
frame->has_this = false;
frame->this_ref = rava_value_null();
return frame;
}
void rava_call_frame_destroy(RavaCallFrame_t *frame) {
if (!frame) return;
free(frame->locals);
rava_stack_destroy(frame->operand_stack);
free(frame);
}
RavaCallStack_t* rava_call_stack_create() {
RavaCallStack_t *stack = malloc(sizeof(RavaCallStack_t));
2025-12-04 06:14:22 +01:00
if (!stack) return NULL;
2025-12-02 06:54:32 +01:00
stack->capacity = 256;
stack->frames = malloc(sizeof(RavaCallFrame_t*) * stack->capacity);
2025-12-04 06:14:22 +01:00
if (!stack->frames) {
free(stack);
return NULL;
}
2025-12-02 06:54:32 +01:00
stack->count = 0;
return stack;
}
void rava_call_stack_destroy(RavaCallStack_t *stack) {
if (!stack) return;
for (size_t i = 0; i < stack->count; i++) {
rava_call_frame_destroy(stack->frames[i]);
}
free(stack->frames);
free(stack);
}
2025-12-03 15:01:02 +01:00
#define RAVA_MAX_CALL_STACK_DEPTH 10000
bool rava_call_stack_push(RavaCallStack_t *stack, RavaCallFrame_t *frame) {
if (stack->count >= RAVA_MAX_CALL_STACK_DEPTH) {
return false;
}
2025-12-02 06:54:32 +01:00
if (stack->count >= stack->capacity) {
stack->capacity *= 2;
2025-12-04 06:14:22 +01:00
RavaCallFrame_t **new_frames = rava_safe_realloc(stack->frames, sizeof(RavaCallFrame_t*) * stack->capacity);
if (!new_frames) return false;
stack->frames = new_frames;
2025-12-02 06:54:32 +01:00
}
stack->frames[stack->count++] = frame;
2025-12-03 15:01:02 +01:00
return true;
2025-12-02 06:54:32 +01:00
}
RavaCallFrame_t* rava_call_stack_pop(RavaCallStack_t *stack) {
if (stack->count == 0) return NULL;
RavaCallFrame_t *frame = stack->frames[--stack->count];
return frame;
}
RavaCallFrame_t* rava_call_stack_current(RavaCallStack_t *stack) {
if (stack->count == 0) return NULL;
return stack->frames[stack->count - 1];
}
2025-12-03 15:01:02 +01:00
static inline uint32_t _rava_static_hash(const char *class_name, const char *field_name) {
uint32_t hash = 5381;
while (*class_name) {
hash = ((hash << 5) + hash) ^ (uint32_t)*class_name++;
}
hash = ((hash << 5) + hash) ^ '.';
while (*field_name) {
hash = ((hash << 5) + hash) ^ (uint32_t)*field_name++;
}
2025-12-04 06:14:22 +01:00
return hash;
2025-12-03 15:01:02 +01:00
}
2025-12-02 06:54:32 +01:00
static RavaStaticFieldTable_t* rava_static_field_table_create() {
RavaStaticFieldTable_t *table = malloc(sizeof(RavaStaticFieldTable_t));
2025-12-04 06:14:22 +01:00
if (!table) return NULL;
2025-12-02 06:54:32 +01:00
table->capacity = 16;
table->fields = calloc(table->capacity, sizeof(RavaStaticField_t));
2025-12-04 06:14:22 +01:00
if (!table->fields) {
free(table);
return NULL;
}
2025-12-02 06:54:32 +01:00
table->count = 0;
2025-12-03 15:01:02 +01:00
for (int i = 0; i < RAVA_STATIC_HASH_SIZE; i++) {
table->hash_table[i] = -1;
}
2025-12-02 06:54:32 +01:00
return table;
}
static void rava_static_field_table_destroy(RavaStaticFieldTable_t *table) {
if (!table) return;
for (size_t i = 0; i < table->count; i++) {
free(table->fields[i].class_name);
free(table->fields[i].field_name);
}
free(table->fields);
free(table);
}
static RavaValue_t* rava_static_field_table_get(RavaStaticFieldTable_t *table,
const char *class_name,
const char *field_name) {
2025-12-04 06:14:22 +01:00
uint32_t hash = _rava_static_hash(class_name, field_name);
for (size_t probe = 0; probe < RAVA_STATIC_HASH_SIZE; probe++) {
uint32_t slot = (hash + probe) & (RAVA_STATIC_HASH_SIZE - 1);
int idx = table->hash_table[slot];
if (idx == -1) {
return NULL;
}
if ((size_t)idx < table->count &&
strcmp(table->fields[idx].class_name, class_name) == 0 &&
strcmp(table->fields[idx].field_name, field_name) == 0) {
return &table->fields[idx].value;
2025-12-02 06:54:32 +01:00
}
}
return NULL;
}
static void rava_static_field_table_set(RavaStaticFieldTable_t *table,
const char *class_name,
const char *field_name,
RavaValue_t value) {
2025-12-04 06:14:22 +01:00
uint32_t hash = _rava_static_hash(class_name, field_name);
for (size_t probe = 0; probe < RAVA_STATIC_HASH_SIZE; probe++) {
uint32_t slot = (hash + probe) & (RAVA_STATIC_HASH_SIZE - 1);
int idx = table->hash_table[slot];
if (idx == -1) {
if (table->count >= table->capacity) {
size_t new_cap = table->capacity * 2;
RavaStaticField_t *new_fields = rava_safe_realloc(table->fields, sizeof(RavaStaticField_t) * new_cap);
if (!new_fields) return;
table->fields = new_fields;
table->capacity = new_cap;
}
table->fields[table->count].class_name = strdup(class_name);
table->fields[table->count].field_name = strdup(field_name);
table->fields[table->count].value = value;
table->hash_table[slot] = (int)table->count;
table->count++;
2025-12-03 15:01:02 +01:00
return;
}
2025-12-02 06:54:32 +01:00
2025-12-04 06:14:22 +01:00
if ((size_t)idx < table->count &&
strcmp(table->fields[idx].class_name, class_name) == 0 &&
strcmp(table->fields[idx].field_name, field_name) == 0) {
table->fields[idx].value = value;
return;
}
2025-12-02 06:54:32 +01:00
}
}
2025-12-02 21:12:50 +01:00
static RavaExceptionStack_t* rava_exception_stack_create() {
RavaExceptionStack_t *stack = calloc(1, sizeof(RavaExceptionStack_t));
2025-12-04 06:14:22 +01:00
if (!stack) return NULL;
2025-12-02 21:12:50 +01:00
stack->count = 0;
return stack;
}
static void rava_exception_stack_destroy(RavaExceptionStack_t *stack) {
free(stack);
}
static void rava_exception_stack_push(RavaExceptionStack_t *stack, RavaExceptionHandler_t handler) {
2025-12-03 15:01:02 +01:00
if (stack->count >= RAVA_MAX_EXCEPTION_DEPTH) return;
2025-12-02 21:12:50 +01:00
stack->handlers[stack->count++] = handler;
}
static bool rava_exception_stack_pop(RavaExceptionStack_t *stack, RavaExceptionHandler_t *handler) {
if (stack->count == 0) return false;
*handler = stack->handlers[--stack->count];
return true;
}
2025-12-03 15:01:02 +01:00
static inline uint32_t _rava_class_hash(const char *name) {
uint32_t hash = 5381;
while (*name) {
hash = ((hash << 5) + hash) ^ (uint32_t)*name++;
}
return hash & (RAVA_CLASS_HASH_SIZE - 1);
}
2025-12-02 06:54:32 +01:00
RavaVM_t* rava_vm_create(RavaProgram_t *program) {
RavaVM_t *vm = calloc(1, sizeof(RavaVM_t));
2025-12-04 06:14:22 +01:00
if (!vm) return NULL;
2025-12-02 06:54:32 +01:00
vm->program = program;
vm->call_stack = rava_call_stack_create();
vm->static_fields = rava_static_field_table_create();
vm->method_cache = rava_methodcache_create();
2025-12-04 06:14:22 +01:00
vm->exception_stack = rava_exception_stack_create();
2025-12-04 23:51:05 +01:00
vm->native_registry = rava_native_registry_create();
if (!vm->call_stack || !vm->static_fields || !vm->method_cache || !vm->exception_stack || !vm->native_registry) {
2025-12-04 06:14:22 +01:00
rava_vm_destroy(vm);
return NULL;
}
2025-12-02 06:54:32 +01:00
vm->error_message = NULL;
vm->had_error = false;
2025-12-02 21:12:50 +01:00
vm->has_exception = false;
vm->exception_value = rava_value_null();
2025-12-03 15:01:02 +01:00
for (size_t i = 0; i < RAVA_CLASS_HASH_SIZE; i++) {
vm->class_hash[i] = -1;
}
for (size_t i = 0; i < program->class_count; i++) {
uint32_t h = _rava_class_hash(program->classes[i]->name);
vm->class_hash[h] = (int)i;
}
2025-12-02 06:54:32 +01:00
return vm;
}
void rava_vm_destroy(RavaVM_t *vm) {
if (!vm) return;
rava_call_stack_destroy(vm->call_stack);
rava_static_field_table_destroy(vm->static_fields);
if (vm->method_cache) {
rava_methodcache_destroy((MethodCache_t*)vm->method_cache);
}
2025-12-02 21:12:50 +01:00
rava_exception_stack_destroy(vm->exception_stack);
2025-12-04 23:51:05 +01:00
rava_native_registry_destroy(vm->native_registry);
2025-12-03 17:55:37 +01:00
for (size_t i = 0; i < RAVA_INTERN_TABLE_SIZE; i++) {
free(vm->intern_table.strings[i]);
}
2025-12-02 06:54:32 +01:00
free(vm->error_message);
free(vm);
}
static RavaClass_t* _rava_vm_find_class(RavaVM_t *vm, const char *class_name) {
2025-12-03 15:01:02 +01:00
uint32_t h = _rava_class_hash(class_name);
int idx = vm->class_hash[h];
if (idx >= 0 && (size_t)idx < vm->program->class_count &&
strcmp(vm->program->classes[idx]->name, class_name) == 0) {
return vm->program->classes[idx];
}
2025-12-02 06:54:32 +01:00
for (size_t i = 0; i < vm->program->class_count; i++) {
if (strcmp(vm->program->classes[i]->name, class_name) == 0) {
2025-12-03 15:01:02 +01:00
vm->class_hash[h] = (int)i;
2025-12-02 06:54:32 +01:00
return vm->program->classes[i];
}
}
return NULL;
}
2025-12-03 17:55:37 +01:00
static inline uint32_t _rava_method_hash(const char *name) {
uint32_t hash = 5381;
while (*name) hash = ((hash << 5) + hash) ^ (uint32_t)*name++;
return hash & (RAVA_METHOD_HASH_SIZE - 1);
}
2025-12-02 06:54:32 +01:00
static RavaMethod_t* _rava_vm_find_method(RavaVM_t *vm, const char *class_name, const char *method_name) {
const char *current_class = class_name;
while (current_class) {
RavaClass_t *class = _rava_vm_find_class(vm, current_class);
if (!class) break;
2025-12-03 17:55:37 +01:00
uint32_t h = _rava_method_hash(method_name);
int idx = class->method_hash[h];
if (idx >= 0 && (size_t)idx < class->method_count &&
strcmp(class->methods[idx]->name, method_name) == 0) {
return class->methods[idx];
}
2025-12-02 06:54:32 +01:00
for (size_t j = 0; j < class->method_count; j++) {
if (strcmp(class->methods[j]->name, method_name) == 0) {
2025-12-03 17:55:37 +01:00
class->method_hash[h] = (int)j;
2025-12-02 06:54:32 +01:00
return class->methods[j];
}
}
current_class = class->superclass;
}
return NULL;
}
static RavaMethod_t* _rava_vm_find_method_cached(RavaVM_t *vm, const char *class_name, const char *method_name) {
MethodCache_t *cache = (MethodCache_t*)vm->method_cache;
RavaMethod_t *cached = rava_methodcache_lookup(cache, class_name, method_name);
if (cached) return cached;
RavaMethod_t *method = _rava_vm_find_method(vm, class_name, method_name);
if (method) {
rava_methodcache_insert(cache, class_name, method_name, method);
}
return method;
}
#ifndef __GNUC__
static bool _rava_vm_execute_instruction(RavaVM_t *vm, RavaCallFrame_t *frame, RavaInstruction_t *instr) {
RavaStack_t *stack = frame->operand_stack;
switch (instr->opcode) {
case RAVA_OP_NOP:
break;
case RAVA_OP_LOAD_CONST:
rava_stack_push(stack, rava_value_int((int32_t)instr->operand.int_value));
break;
case RAVA_OP_LOAD_LONG:
rava_stack_push(stack, rava_value_long(instr->operand.int_value));
break;
case RAVA_OP_LOAD_DOUBLE:
rava_stack_push(stack, rava_value_double(instr->operand.float_value));
break;
case RAVA_OP_LOAD_STRING:
rava_stack_push(stack, rava_value_string(instr->operand.string_value));
break;
case RAVA_OP_LOAD_LOCAL:
if ((size_t)instr->operand.var.index < frame->local_count) {
rava_stack_push(stack, frame->locals[instr->operand.var.index]);
}
break;
case RAVA_OP_LOAD_THIS:
if (frame->has_this) {
rava_stack_push(stack, frame->this_ref);
} else {
rava_stack_push(stack, rava_value_null());
}
break;
case RAVA_OP_STORE_LOCAL:
if ((size_t)instr->operand.var.index < frame->local_count) {
frame->locals[instr->operand.var.index] = rava_stack_pop(stack);
}
break;
case RAVA_OP_LOAD_STATIC: {
RavaValue_t *field_val = rava_static_field_table_get(
vm->static_fields,
instr->operand.field.class_name,
instr->operand.field.field_name
);
if (field_val) {
rava_stack_push(stack, *field_val);
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_STORE_STATIC: {
RavaValue_t value = rava_stack_pop(stack);
rava_static_field_table_set(
vm->static_fields,
instr->operand.field.class_name,
instr->operand.field.field_name,
value
);
break;
}
case RAVA_OP_ADD: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_STRING || b.type == RAVA_VAL_STRING) {
char buf_a[64], buf_b[64];
const char *str_a, *str_b;
if (a.type == RAVA_VAL_STRING) {
str_a = a.data.string_val ? a.data.string_val : "null";
} else if (a.type == RAVA_VAL_LONG) {
snprintf(buf_a, sizeof(buf_a), "%ld", (long)rava_value_as_long(a));
str_a = buf_a;
} else {
snprintf(buf_a, sizeof(buf_a), "%d", rava_value_as_int(a));
str_a = buf_a;
}
if (b.type == RAVA_VAL_STRING) {
str_b = b.data.string_val ? b.data.string_val : "null";
} else if (b.type == RAVA_VAL_LONG) {
snprintf(buf_b, sizeof(buf_b), "%ld", (long)rava_value_as_long(b));
str_b = buf_b;
} else {
snprintf(buf_b, sizeof(buf_b), "%d", rava_value_as_int(b));
str_b = buf_b;
}
size_t len = strlen(str_a) + strlen(str_b) + 1;
char *result = malloc(len);
strcpy(result, str_a);
strcat(result, str_b);
rava_stack_push(stack, rava_value_string(result));
free(result);
} else if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) {
rava_stack_push(stack, rava_value_long(rava_value_as_long(a) + rava_value_as_long(b)));
} else {
rava_stack_push(stack, rava_value_int(rava_value_as_int(a) + rava_value_as_int(b)));
}
break;
}
case RAVA_OP_SUB: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) {
rava_stack_push(stack, rava_value_long(rava_value_as_long(a) - rava_value_as_long(b)));
} else {
rava_stack_push(stack, rava_value_int(rava_value_as_int(a) - rava_value_as_int(b)));
}
break;
}
case RAVA_OP_MUL: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) {
rava_stack_push(stack, rava_value_long(rava_value_as_long(a) * rava_value_as_long(b)));
} else {
rava_stack_push(stack, rava_value_int(rava_value_as_int(a) * rava_value_as_int(b)));
}
break;
}
case RAVA_OP_DIV: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) {
int64_t divisor = rava_value_as_long(b);
if (divisor == 0) {
vm->had_error = true;
vm->error_message = strdup("Division by zero");
return false;
}
rava_stack_push(stack, rava_value_long(rava_value_as_long(a) / divisor));
} else {
int32_t divisor = rava_value_as_int(b);
if (divisor == 0) {
vm->had_error = true;
vm->error_message = strdup("Division by zero");
return false;
}
rava_stack_push(stack, rava_value_int(rava_value_as_int(a) / divisor));
}
break;
}
case RAVA_OP_MOD: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) {
2025-12-03 15:01:02 +01:00
int64_t divisor = rava_value_as_long(b);
if (divisor == 0) {
vm->had_error = true;
vm->error_message = strdup("Division by zero");
return false;
}
rava_stack_push(stack, rava_value_long(rava_value_as_long(a) % divisor));
2025-12-02 06:54:32 +01:00
} else {
2025-12-03 15:01:02 +01:00
int32_t divisor = rava_value_as_int(b);
if (divisor == 0) {
vm->had_error = true;
vm->error_message = strdup("Division by zero");
return false;
}
rava_stack_push(stack, rava_value_int(rava_value_as_int(a) % divisor));
2025-12-02 06:54:32 +01:00
}
break;
}
case RAVA_OP_NEG: {
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_int(-rava_value_as_int(a)));
break;
}
case RAVA_OP_EQ: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) == rava_value_as_int(b)));
break;
}
case RAVA_OP_NE: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) != rava_value_as_int(b)));
break;
}
case RAVA_OP_LT: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) < rava_value_as_int(b)));
break;
}
case RAVA_OP_LE: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) <= rava_value_as_int(b)));
break;
}
case RAVA_OP_GT: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) > rava_value_as_int(b)));
break;
}
case RAVA_OP_GE: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) >= rava_value_as_int(b)));
break;
}
case RAVA_OP_AND: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
2025-12-02 21:12:50 +01:00
rava_stack_push(stack, rava_value_int(rava_value_as_int(a) & rava_value_as_int(b)));
2025-12-02 06:54:32 +01:00
break;
}
case RAVA_OP_OR: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
2025-12-02 21:12:50 +01:00
rava_stack_push(stack, rava_value_int(rava_value_as_int(a) | rava_value_as_int(b)));
2025-12-02 06:54:32 +01:00
break;
}
case RAVA_OP_NOT: {
RavaValue_t a = rava_stack_pop(stack);
rava_stack_push(stack, rava_value_boolean(!rava_value_as_boolean(a)));
break;
}
case RAVA_OP_JUMP: {
if (!frame->method->label_table) {
frame->method->label_table = rava_labeltable_create(frame->method->instructions);
}
size_t target_pc = rava_labeltable_lookup((LabelTable_t*)frame->method->label_table, instr->operand.label_id);
if (target_pc > 0) {
frame->pc = target_pc - 1;
return true;
}
break;
}
case RAVA_OP_JUMP_IF_TRUE: {
RavaValue_t cond = rava_stack_pop(stack);
if (rava_value_as_boolean(cond)) {
if (!frame->method->label_table) {
frame->method->label_table = rava_labeltable_create(frame->method->instructions);
}
size_t target_pc = rava_labeltable_lookup((LabelTable_t*)frame->method->label_table, instr->operand.label_id);
if (target_pc > 0) {
frame->pc = target_pc - 1;
return true;
}
}
break;
}
case RAVA_OP_JUMP_IF_FALSE: {
RavaValue_t cond = rava_stack_pop(stack);
if (!rava_value_as_boolean(cond)) {
if (!frame->method->label_table) {
frame->method->label_table = rava_labeltable_create(frame->method->instructions);
}
size_t target_pc = rava_labeltable_lookup((LabelTable_t*)frame->method->label_table, instr->operand.label_id);
if (target_pc > 0) {
frame->pc = target_pc - 1;
return true;
}
}
break;
}
case RAVA_OP_LABEL:
break;
case RAVA_OP_CALL_STATIC: {
RavaMethod_t *method = _rava_vm_find_method_cached(vm,
instr->operand.call.class_name,
instr->operand.call.method_name);
if (!method) {
vm->had_error = true;
2025-12-04 06:14:22 +01:00
vm->error_message = malloc(RAVA_ERROR_BUFFER_SIZE);
snprintf(vm->error_message, RAVA_ERROR_BUFFER_SIZE, "Method not found: %s.%s",
2025-12-02 06:54:32 +01:00
instr->operand.call.class_name,
instr->operand.call.method_name);
return false;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(method);
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
new_frame->locals[i] = rava_stack_pop(stack);
}
2025-12-03 15:01:02 +01:00
if (!rava_call_stack_push(vm->call_stack, new_frame)) {
vm->had_error = true;
vm->error_message = strdup("Stack overflow");
rava_call_frame_destroy(new_frame);
return false;
}
2025-12-02 06:54:32 +01:00
while (new_frame->pc < new_frame->method->instructions->count) {
RavaInstruction_t *next_instr = &new_frame->method->instructions->instructions[new_frame->pc];
if (next_instr->opcode == RAVA_OP_RETURN || next_instr->opcode == RAVA_OP_RETURN_VOID) {
RavaValue_t result = rava_value_int(0);
if (next_instr->opcode == RAVA_OP_RETURN && !rava_stack_is_empty(new_frame->operand_stack)) {
result = rava_stack_pop(new_frame->operand_stack);
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
if (next_instr->opcode == RAVA_OP_RETURN) {
rava_stack_push(stack, result);
}
break;
}
new_frame->pc++;
if (!_rava_vm_execute_instruction(vm, new_frame, next_instr)) {
return false;
}
}
break;
}
case RAVA_OP_CALL_VIRTUAL: {
RavaValue_t args[16];
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
args[i] = rava_stack_pop(stack);
}
RavaValue_t obj_val = rava_stack_pop(stack);
const char *class_name = instr->operand.call.class_name;
if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) {
class_name = obj_val.data.object_val->class_name;
}
RavaMethod_t *method = _rava_vm_find_method_cached(vm, class_name, instr->operand.call.method_name);
if (!method) {
if (strcmp(instr->operand.call.method_name, "<init>") == 0 && instr->operand.call.arg_count == 0) {
break;
}
2025-12-04 23:51:05 +01:00
const char *mname = instr->operand.call.method_name;
if (strcmp(mname, "hashCode") == 0 && instr->operand.call.arg_count == 0) {
rava_stack_push(stack, rava_value_int(rava_object_hashcode(obj_val)));
break;
}
if (strcmp(mname, "equals") == 0 && instr->operand.call.arg_count == 1) {
rava_stack_push(stack, rava_value_boolean(rava_object_equals(obj_val, args[0])));
break;
}
if (strcmp(mname, "toString") == 0 && instr->operand.call.arg_count == 0) {
char *str = rava_object_tostring(obj_val);
rava_stack_push(stack, rava_value_string(str));
free(str);
break;
}
if (strcmp(mname, "getClass") == 0 && instr->operand.call.arg_count == 0) {
rava_stack_push(stack, rava_value_string(rava_object_getclass(obj_val)));
break;
}
2025-12-02 06:54:32 +01:00
vm->had_error = true;
2025-12-04 06:14:22 +01:00
vm->error_message = malloc(RAVA_ERROR_BUFFER_SIZE);
snprintf(vm->error_message, RAVA_ERROR_BUFFER_SIZE, "Method not found: %s.%s",
2025-12-02 06:54:32 +01:00
class_name, instr->operand.call.method_name);
return false;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(method);
new_frame->has_this = true;
new_frame->this_ref = obj_val;
for (int i = 0; i < instr->operand.call.arg_count; i++) {
new_frame->locals[i] = args[i];
}
2025-12-03 15:01:02 +01:00
if (!rava_call_stack_push(vm->call_stack, new_frame)) {
vm->had_error = true;
vm->error_message = strdup("Stack overflow");
rava_call_frame_destroy(new_frame);
return false;
}
2025-12-02 06:54:32 +01:00
while (new_frame->pc < new_frame->method->instructions->count) {
RavaInstruction_t *next_instr = &new_frame->method->instructions->instructions[new_frame->pc];
if (next_instr->opcode == RAVA_OP_RETURN || next_instr->opcode == RAVA_OP_RETURN_VOID) {
RavaValue_t result = rava_value_int(0);
if (next_instr->opcode == RAVA_OP_RETURN && !rava_stack_is_empty(new_frame->operand_stack)) {
result = rava_stack_pop(new_frame->operand_stack);
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
if (next_instr->opcode == RAVA_OP_RETURN) {
rava_stack_push(stack, result);
}
break;
}
new_frame->pc++;
if (!_rava_vm_execute_instruction(vm, new_frame, next_instr)) {
return false;
}
}
break;
}
2025-12-02 21:12:50 +01:00
case RAVA_OP_CALL_SUPER: {
RavaValue_t args[16];
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
args[i] = rava_stack_pop(stack);
}
RavaValue_t obj_val = rava_stack_pop(stack);
const char *super_class = instr->operand.call.class_name;
RavaMethod_t *method = _rava_vm_find_method_cached(vm, super_class, instr->operand.call.method_name);
if (!method) {
vm->had_error = true;
2025-12-04 06:14:22 +01:00
vm->error_message = malloc(RAVA_ERROR_BUFFER_SIZE);
snprintf(vm->error_message, RAVA_ERROR_BUFFER_SIZE, "Super method not found: %s.%s",
2025-12-02 21:12:50 +01:00
super_class, instr->operand.call.method_name);
return false;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(method);
new_frame->has_this = true;
new_frame->this_ref = obj_val;
for (int i = 0; i < instr->operand.call.arg_count; i++) {
new_frame->locals[i] = args[i];
}
2025-12-03 15:01:02 +01:00
if (!rava_call_stack_push(vm->call_stack, new_frame)) {
vm->had_error = true;
vm->error_message = strdup("Stack overflow");
rava_call_frame_destroy(new_frame);
return false;
}
2025-12-02 21:12:50 +01:00
while (new_frame->pc < new_frame->method->instructions->count) {
RavaInstruction_t *next_instr = &new_frame->method->instructions->instructions[new_frame->pc];
if (next_instr->opcode == RAVA_OP_RETURN || next_instr->opcode == RAVA_OP_RETURN_VOID) {
RavaValue_t result = rava_value_int(0);
if (next_instr->opcode == RAVA_OP_RETURN && !rava_stack_is_empty(new_frame->operand_stack)) {
result = rava_stack_pop(new_frame->operand_stack);
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
if (next_instr->opcode == RAVA_OP_RETURN) {
rava_stack_push(stack, result);
}
break;
}
new_frame->pc++;
if (!_rava_vm_execute_instruction(vm, new_frame, next_instr)) {
return false;
}
}
break;
}
2025-12-02 06:54:32 +01:00
case RAVA_OP_RETURN:
case RAVA_OP_RETURN_VOID:
return true;
2025-12-02 21:12:50 +01:00
case RAVA_OP_THROW: {
RavaValue_t exception = rava_stack_pop(stack);
vm->has_exception = true;
vm->exception_value = exception;
RavaExceptionHandler_t handler;
if (rava_exception_stack_pop(vm->exception_stack, &handler)) {
locals[handler.exception_local] = exception;
pc = label_table_lookup(method->label_table, handler.catch_label);
vm->has_exception = false;
} else {
return false;
}
break;
}
case RAVA_OP_TRY_BEGIN: {
RavaExceptionHandler_t handler;
handler.catch_label = instr->operand.try_handler.catch_label;
handler.finally_label = instr->operand.try_handler.finally_label;
handler.end_label = instr->operand.try_handler.end_label;
handler.exception_local = instr->operand.try_handler.exception_local;
handler.stack_depth = stack->top;
rava_exception_stack_push(vm->exception_stack, handler);
break;
}
case RAVA_OP_TRY_END: {
RavaExceptionHandler_t handler;
rava_exception_stack_pop(vm->exception_stack, &handler);
break;
}
2025-12-02 06:54:32 +01:00
case RAVA_OP_POP:
rava_stack_pop(stack);
break;
case RAVA_OP_DUP:
rava_stack_push(stack, rava_stack_peek(stack));
break;
case RAVA_OP_PRINT: {
RavaValue_t value = rava_stack_pop(stack);
switch (value.type) {
case RAVA_VAL_STRING:
printf("%s", value.data.string_val ? value.data.string_val : "null");
break;
case RAVA_VAL_BOOLEAN:
printf("%s", value.data.bool_val ? "true" : "false");
break;
case RAVA_VAL_DOUBLE:
printf("%g", value.data.double_val);
break;
case RAVA_VAL_FLOAT:
printf("%g", (double)value.data.float_val);
break;
case RAVA_VAL_LONG:
printf("%ld", (long)value.data.long_val);
break;
case RAVA_VAL_NULL:
printf("null");
break;
case RAVA_VAL_ARRAY:
printf("[array@%p]", (void*)value.data.array_val);
break;
default:
printf("%d", rava_value_as_int(value));
break;
}
fflush(stdout);
break;
}
case RAVA_OP_PRINTLN: {
RavaValue_t value = rava_stack_pop(stack);
switch (value.type) {
case RAVA_VAL_STRING:
printf("%s\n", value.data.string_val ? value.data.string_val : "null");
break;
case RAVA_VAL_BOOLEAN:
printf("%s\n", value.data.bool_val ? "true" : "false");
break;
case RAVA_VAL_DOUBLE:
printf("%g\n", value.data.double_val);
break;
case RAVA_VAL_FLOAT:
printf("%g\n", (double)value.data.float_val);
break;
case RAVA_VAL_LONG:
printf("%ld\n", (long)value.data.long_val);
break;
case RAVA_VAL_NULL:
printf("null\n");
break;
case RAVA_VAL_ARRAY:
printf("[array@%p]\n", (void*)value.data.array_val);
break;
default:
printf("%d\n", rava_value_as_int(value));
break;
}
fflush(stdout);
break;
}
case RAVA_OP_NEW: {
const char *class_name = instr->operand.call.class_name;
RavaObject_t *obj = rava_object_create(class_name);
if (obj) {
rava_stack_push(stack, rava_value_object(obj));
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
case RAVA_OP_NEW_ARRAY: {
RavaValue_t length_val = rava_stack_pop(stack);
int32_t length = rava_value_as_int(length_val);
if (length < 0) length = 0;
RavaArray_t *array = rava_array_create(RAVA_VAL_INT, (size_t)length);
if (array) {
rava_stack_push(stack, rava_value_array(array));
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
2025-12-03 14:08:55 +01:00
case RAVA_OP_NEW_ARRAY_OF_ARRAYS: {
RavaValue_t length_val = rava_stack_pop(stack);
int32_t length = rava_value_as_int(length_val);
if (length < 0) length = 0;
RavaArray_t *array = rava_array_create(RAVA_VAL_ARRAY, (size_t)length);
if (array) {
rava_stack_push(stack, rava_value_array(array));
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
2025-12-02 06:54:32 +01:00
case RAVA_OP_GET_FIELD: {
RavaValue_t obj_val = rava_stack_pop(stack);
if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) {
RavaValue_t field_val = rava_object_get_field(obj_val.data.object_val, instr->operand.field.field_name);
rava_stack_push(stack, field_val);
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
case RAVA_OP_PUT_FIELD: {
RavaValue_t value = rava_stack_pop(stack);
RavaValue_t obj_val = rava_stack_pop(stack);
if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) {
2025-12-03 17:55:37 +01:00
_rava_object_set_field_vm(obj_val.data.object_val, instr->operand.field.field_name, value, vm);
2025-12-02 06:54:32 +01:00
}
break;
}
case RAVA_OP_ARRAY_LENGTH: {
RavaValue_t array_val = rava_stack_pop(stack);
if (array_val.type == RAVA_VAL_ARRAY) {
size_t length = rava_array_length(array_val.data.array_val);
rava_stack_push(stack, rava_value_int((int32_t)length));
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_LOAD_ARRAY: {
RavaValue_t index_val = rava_stack_pop(stack);
RavaValue_t array_val = rava_stack_pop(stack);
if (array_val.type == RAVA_VAL_ARRAY) {
int32_t index = rava_value_as_int(index_val);
2025-12-03 14:08:55 +01:00
RavaArray_t *arr = array_val.data.array_val;
if (arr->element_type == RAVA_VAL_ARRAY || arr->element_type == RAVA_VAL_OBJECT) {
rava_stack_push(stack, rava_array_get_value(arr, (size_t)index));
} else {
int32_t value = rava_array_get_int(arr, (size_t)index);
rava_stack_push(stack, rava_value_int(value));
}
2025-12-02 06:54:32 +01:00
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_STORE_ARRAY: {
RavaValue_t value_val = rava_stack_pop(stack);
RavaValue_t index_val = rava_stack_pop(stack);
RavaValue_t array_val = rava_stack_pop(stack);
if (array_val.type == RAVA_VAL_ARRAY) {
int32_t index = rava_value_as_int(index_val);
2025-12-03 14:08:55 +01:00
RavaArray_t *arr = array_val.data.array_val;
if (arr->element_type == RAVA_VAL_ARRAY || arr->element_type == RAVA_VAL_OBJECT) {
rava_array_set_value(arr, (size_t)index, value_val);
} else {
int32_t value = rava_value_as_int(value_val);
rava_array_set_int(arr, (size_t)index, value);
}
2025-12-02 06:54:32 +01:00
}
break;
}
case RAVA_OP_STRING_LENGTH: {
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) {
rava_stack_push(stack, rava_value_int((int32_t)strlen(str_val.data.string_val)));
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_STRING_CHARAT: {
RavaValue_t index_val = rava_stack_pop(stack);
RavaValue_t str_val = rava_stack_pop(stack);
int32_t index = rava_value_as_int(index_val);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) {
size_t len = strlen(str_val.data.string_val);
if (index >= 0 && (size_t)index < len) {
RavaValue_t char_val;
char_val.type = RAVA_VAL_CHAR;
char_val.data.char_val = str_val.data.string_val[index];
rava_stack_push(stack, char_val);
} else {
rava_stack_push(stack, rava_value_int(0));
}
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_STRING_EQUALS: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
bool result = false;
if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING) {
if (a.data.string_val && b.data.string_val) {
result = strcmp(a.data.string_val, b.data.string_val) == 0;
} else {
result = (a.data.string_val == b.data.string_val);
}
}
rava_stack_push(stack, rava_value_boolean(result));
break;
}
2025-12-02 21:12:50 +01:00
case RAVA_OP_STRING_SUBSTRING: {
RavaValue_t end_val = rava_stack_pop(stack);
RavaValue_t start_val = rava_stack_pop(stack);
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) {
size_t len = strlen(str_val.data.string_val);
size_t start = (size_t)rava_value_as_int(start_val);
size_t end = (size_t)rava_value_as_int(end_val);
if (start > len) start = len;
if (end > len) end = len;
if (start > end) start = end;
size_t sub_len = end - start;
char *sub = malloc(sub_len + 1);
strncpy(sub, str_val.data.string_val + start, sub_len);
sub[sub_len] = '\0';
rava_stack_push(stack, rava_value_string(sub));
} else {
rava_stack_push(stack, rava_value_string(""));
}
break;
}
case RAVA_OP_STRING_COMPARETO: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING &&
a.data.string_val && b.data.string_val) {
rava_stack_push(stack, rava_value_int(strcmp(a.data.string_val, b.data.string_val)));
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_STRING_INDEXOF: {
RavaValue_t needle_val = rava_stack_pop(stack);
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val &&
needle_val.type == RAVA_VAL_STRING && needle_val.data.string_val) {
const char *found = strstr(str_val.data.string_val, needle_val.data.string_val);
rava_stack_push(stack, rava_value_int(found ? (int32_t)(found - str_val.data.string_val) : -1));
} else {
rava_stack_push(stack, rava_value_int(-1));
}
break;
}
case RAVA_OP_STRING_CONTAINS: {
RavaValue_t needle_val = rava_stack_pop(stack);
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val &&
needle_val.type == RAVA_VAL_STRING && needle_val.data.string_val) {
rava_stack_push(stack, rava_value_boolean(strstr(str_val.data.string_val, needle_val.data.string_val) != NULL));
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
break;
}
case RAVA_OP_STRING_STARTSWITH: {
RavaValue_t prefix_val = rava_stack_pop(stack);
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val &&
prefix_val.type == RAVA_VAL_STRING && prefix_val.data.string_val) {
size_t plen = strlen(prefix_val.data.string_val);
rava_stack_push(stack, rava_value_boolean(strncmp(str_val.data.string_val, prefix_val.data.string_val, plen) == 0));
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
break;
}
case RAVA_OP_STRING_ENDSWITH: {
RavaValue_t suffix_val = rava_stack_pop(stack);
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val &&
suffix_val.type == RAVA_VAL_STRING && suffix_val.data.string_val) {
size_t slen = strlen(str_val.data.string_val);
size_t suflen = strlen(suffix_val.data.string_val);
if (suflen <= slen) {
rava_stack_push(stack, rava_value_boolean(strcmp(str_val.data.string_val + slen - suflen, suffix_val.data.string_val) == 0));
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
break;
}
case RAVA_OP_STRING_TOLOWERCASE: {
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) {
size_t len = strlen(str_val.data.string_val);
char *lower = malloc(len + 1);
for (size_t i = 0; i <= len; i++) {
lower[i] = (char)tolower((unsigned char)str_val.data.string_val[i]);
}
rava_stack_push(stack, rava_value_string(lower));
} else {
rava_stack_push(stack, rava_value_string(""));
}
break;
}
case RAVA_OP_STRING_TOUPPERCASE: {
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) {
size_t len = strlen(str_val.data.string_val);
char *upper = malloc(len + 1);
for (size_t i = 0; i <= len; i++) {
upper[i] = (char)toupper((unsigned char)str_val.data.string_val[i]);
}
rava_stack_push(stack, rava_value_string(upper));
} else {
rava_stack_push(stack, rava_value_string(""));
}
break;
}
case RAVA_OP_STRING_TRIM: {
RavaValue_t str_val = rava_stack_pop(stack);
if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) {
const char *start = str_val.data.string_val;
while (*start && isspace((unsigned char)*start)) start++;
const char *end = str_val.data.string_val + strlen(str_val.data.string_val);
while (end > start && isspace((unsigned char)*(end - 1))) end--;
size_t len = (size_t)(end - start);
char *trimmed = malloc(len + 1);
strncpy(trimmed, start, len);
trimmed[len] = '\0';
rava_stack_push(stack, rava_value_string(trimmed));
} else {
rava_stack_push(stack, rava_value_string(""));
}
2025-12-02 06:54:32 +01:00
break;
2025-12-02 21:12:50 +01:00
}
2025-12-02 06:54:32 +01:00
case RAVA_OP_FILE_READ: {
RavaValue_t path_val = rava_stack_pop(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) {
FILE *file = fopen(path_val.data.string_val, "r");
if (file) {
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char *content = malloc(size + 1);
if (content) {
size_t read = fread(content, 1, size, file);
content[read] = '\0';
rava_stack_push(stack, rava_value_string(content));
free(content);
} else {
rava_stack_push(stack, rava_value_null());
}
fclose(file);
} else {
rava_stack_push(stack, rava_value_null());
}
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
case RAVA_OP_FILE_WRITE: {
RavaValue_t content_val = rava_stack_pop(stack);
RavaValue_t path_val = rava_stack_pop(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val &&
content_val.type == RAVA_VAL_STRING && content_val.data.string_val) {
FILE *file = fopen(path_val.data.string_val, "w");
if (file) {
fputs(content_val.data.string_val, file);
fclose(file);
rava_stack_push(stack, rava_value_boolean(true));
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
break;
}
case RAVA_OP_FILE_EXISTS: {
RavaValue_t path_val = rava_stack_pop(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) {
FILE *file = fopen(path_val.data.string_val, "r");
if (file) {
fclose(file);
rava_stack_push(stack, rava_value_boolean(true));
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
break;
}
case RAVA_OP_FILE_DELETE: {
RavaValue_t path_val = rava_stack_pop(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) {
int result = remove(path_val.data.string_val);
rava_stack_push(stack, rava_value_boolean(result == 0));
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
break;
}
case RAVA_OP_CURRENT_TIME_MILLIS: {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
int64_t millis = (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
rava_stack_push(stack, rava_value_long(millis));
break;
}
case RAVA_OP_NANO_TIME: {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int64_t nanos = (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec;
rava_stack_push(stack, rava_value_long(nanos));
break;
}
2025-12-02 21:12:50 +01:00
case RAVA_OP_MATH_ABS: {
RavaValue_t val = rava_stack_pop(stack);
if (val.type == RAVA_VAL_DOUBLE) {
rava_stack_push(stack, rava_value_double(fabs(val.data.double_val)));
} else if (val.type == RAVA_VAL_LONG) {
int64_t v = val.data.long_val;
rava_stack_push(stack, rava_value_long(v < 0 ? -v : v));
} else {
int32_t v = rava_value_as_int(val);
rava_stack_push(stack, rava_value_int(v < 0 ? -v : v));
}
break;
}
case RAVA_OP_MATH_SQRT: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(sqrt(d)));
break;
}
case RAVA_OP_MATH_POW: {
RavaValue_t exp = rava_stack_pop(stack);
RavaValue_t base = rava_stack_pop(stack);
double b = (base.type == RAVA_VAL_DOUBLE) ? base.data.double_val : (double)rava_value_as_int(base);
double e = (exp.type == RAVA_VAL_DOUBLE) ? exp.data.double_val : (double)rava_value_as_int(exp);
rava_stack_push(stack, rava_value_double(pow(b, e)));
break;
}
case RAVA_OP_MATH_MIN: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_DOUBLE || b.type == RAVA_VAL_DOUBLE) {
double da = (a.type == RAVA_VAL_DOUBLE) ? a.data.double_val : (double)rava_value_as_int(a);
double db = (b.type == RAVA_VAL_DOUBLE) ? b.data.double_val : (double)rava_value_as_int(b);
rava_stack_push(stack, rava_value_double(da < db ? da : db));
} else {
int32_t ia = rava_value_as_int(a);
int32_t ib = rava_value_as_int(b);
rava_stack_push(stack, rava_value_int(ia < ib ? ia : ib));
}
break;
}
case RAVA_OP_MATH_MAX: {
RavaValue_t b = rava_stack_pop(stack);
RavaValue_t a = rava_stack_pop(stack);
if (a.type == RAVA_VAL_DOUBLE || b.type == RAVA_VAL_DOUBLE) {
double da = (a.type == RAVA_VAL_DOUBLE) ? a.data.double_val : (double)rava_value_as_int(a);
double db = (b.type == RAVA_VAL_DOUBLE) ? b.data.double_val : (double)rava_value_as_int(b);
rava_stack_push(stack, rava_value_double(da > db ? da : db));
} else {
int32_t ia = rava_value_as_int(a);
int32_t ib = rava_value_as_int(b);
rava_stack_push(stack, rava_value_int(ia > ib ? ia : ib));
}
break;
}
case RAVA_OP_MATH_FLOOR: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(floor(d)));
break;
}
case RAVA_OP_MATH_CEIL: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(ceil(d)));
break;
}
case RAVA_OP_MATH_ROUND: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_long((int64_t)round(d)));
break;
}
case RAVA_OP_MATH_SIN: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(sin(d)));
break;
}
case RAVA_OP_MATH_COS: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(cos(d)));
break;
}
case RAVA_OP_MATH_TAN: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(tan(d)));
break;
}
case RAVA_OP_MATH_LOG: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(log(d)));
break;
}
case RAVA_OP_MATH_EXP: {
RavaValue_t val = rava_stack_pop(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
rava_stack_push(stack, rava_value_double(exp(d)));
break;
}
case RAVA_OP_MATH_RANDOM: {
rava_stack_push(stack, rava_value_double((double)rand() / (double)RAND_MAX));
break;
}
2025-12-04 16:49:20 +01:00
case RAVA_OP_ARRAYLIST_NEW: {
RavaArrayList_t *list = rava_arraylist_create();
rava_stack_push(stack, rava_value_arraylist(list));
break;
}
case RAVA_OP_ARRAYLIST_ADD: {
RavaValue_t value = rava_stack_pop(stack);
RavaValue_t list_val = rava_stack_pop(stack);
if (list_val.type == RAVA_VAL_ARRAYLIST) {
rava_arraylist_add(list_val.data.arraylist_val, value);
}
break;
}
case RAVA_OP_ARRAYLIST_GET: {
RavaValue_t index_or_key = rava_stack_pop(stack);
RavaValue_t coll_val = rava_stack_pop(stack);
if (coll_val.type == RAVA_VAL_ARRAYLIST) {
rava_stack_push(stack, rava_arraylist_get(coll_val.data.arraylist_val, (size_t)rava_value_as_int(index_or_key)));
} else if (coll_val.type == RAVA_VAL_HASHMAP && index_or_key.type == RAVA_VAL_STRING) {
rava_stack_push(stack, rava_hashmap_get(coll_val.data.hashmap_val, index_or_key.data.string_val));
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
case RAVA_OP_ARRAYLIST_SET: {
RavaValue_t value = rava_stack_pop(stack);
RavaValue_t index = rava_stack_pop(stack);
RavaValue_t list_val = rava_stack_pop(stack);
if (list_val.type == RAVA_VAL_ARRAYLIST) {
rava_arraylist_set(list_val.data.arraylist_val, (size_t)rava_value_as_int(index), value);
}
break;
}
case RAVA_OP_ARRAYLIST_SIZE: {
RavaValue_t coll_val = rava_stack_pop(stack);
if (coll_val.type == RAVA_VAL_ARRAYLIST) {
rava_stack_push(stack, rava_value_int((int32_t)rava_arraylist_size(coll_val.data.arraylist_val)));
} else if (coll_val.type == RAVA_VAL_HASHMAP) {
rava_stack_push(stack, rava_value_int((int32_t)rava_hashmap_size(coll_val.data.hashmap_val)));
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_ARRAYLIST_REMOVE: {
RavaValue_t index_or_key = rava_stack_pop(stack);
RavaValue_t coll_val = rava_stack_pop(stack);
if (coll_val.type == RAVA_VAL_ARRAYLIST) {
rava_stack_push(stack, rava_arraylist_remove(coll_val.data.arraylist_val, (size_t)rava_value_as_int(index_or_key)));
} else if (coll_val.type == RAVA_VAL_HASHMAP && index_or_key.type == RAVA_VAL_STRING) {
rava_stack_push(stack, rava_hashmap_remove(coll_val.data.hashmap_val, index_or_key.data.string_val));
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
case RAVA_OP_ARRAYLIST_CLEAR: {
RavaValue_t coll_val = rava_stack_pop(stack);
if (coll_val.type == RAVA_VAL_ARRAYLIST) {
rava_arraylist_clear(coll_val.data.arraylist_val);
} else if (coll_val.type == RAVA_VAL_HASHMAP) {
rava_hashmap_clear(coll_val.data.hashmap_val);
}
break;
}
case RAVA_OP_ARRAYLIST_ISEMPTY: {
RavaValue_t list_val = rava_stack_pop(stack);
if (list_val.type == RAVA_VAL_ARRAYLIST) {
rava_stack_push(stack, rava_value_boolean(rava_arraylist_isempty(list_val.data.arraylist_val)));
} else {
rava_stack_push(stack, rava_value_boolean(true));
}
break;
}
case RAVA_OP_HASHMAP_NEW: {
RavaHashMap_t *map = rava_hashmap_create();
rava_stack_push(stack, rava_value_hashmap(map));
break;
}
case RAVA_OP_HASHMAP_PUT: {
RavaValue_t value = rava_stack_pop(stack);
RavaValue_t key_val = rava_stack_pop(stack);
RavaValue_t map_val = rava_stack_pop(stack);
if (map_val.type == RAVA_VAL_HASHMAP && key_val.type == RAVA_VAL_STRING) {
rava_hashmap_put(map_val.data.hashmap_val, key_val.data.string_val, value);
}
break;
}
case RAVA_OP_HASHMAP_GET: {
RavaValue_t key_val = rava_stack_pop(stack);
RavaValue_t map_val = rava_stack_pop(stack);
if (map_val.type == RAVA_VAL_HASHMAP && key_val.type == RAVA_VAL_STRING) {
rava_stack_push(stack, rava_hashmap_get(map_val.data.hashmap_val, key_val.data.string_val));
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
case RAVA_OP_HASHMAP_REMOVE: {
RavaValue_t key_val = rava_stack_pop(stack);
RavaValue_t map_val = rava_stack_pop(stack);
if (map_val.type == RAVA_VAL_HASHMAP && key_val.type == RAVA_VAL_STRING) {
rava_stack_push(stack, rava_hashmap_remove(map_val.data.hashmap_val, key_val.data.string_val));
} else {
rava_stack_push(stack, rava_value_null());
}
break;
}
case RAVA_OP_HASHMAP_SIZE: {
RavaValue_t map_val = rava_stack_pop(stack);
if (map_val.type == RAVA_VAL_HASHMAP) {
rava_stack_push(stack, rava_value_int((int32_t)rava_hashmap_size(map_val.data.hashmap_val)));
} else {
rava_stack_push(stack, rava_value_int(0));
}
break;
}
case RAVA_OP_HASHMAP_CONTAINSKEY: {
RavaValue_t key_val = rava_stack_pop(stack);
RavaValue_t map_val = rava_stack_pop(stack);
if (map_val.type == RAVA_VAL_HASHMAP && key_val.type == RAVA_VAL_STRING) {
rava_stack_push(stack, rava_value_boolean(rava_hashmap_containskey(map_val.data.hashmap_val, key_val.data.string_val)));
} else {
rava_stack_push(stack, rava_value_boolean(false));
}
break;
}
case RAVA_OP_HASHMAP_CLEAR: {
RavaValue_t map_val = rava_stack_pop(stack);
if (map_val.type == RAVA_VAL_HASHMAP) {
rava_hashmap_clear(map_val.data.hashmap_val);
}
break;
}
2025-12-02 21:12:50 +01:00
case RAVA_OP_CAST: {
RavaValue_t val = rava_stack_pop(stack);
RavaType_t *target_type = instr->operand.var.type;
if (target_type) {
switch (target_type->kind) {
case RAVA_TYPE_INT:
if (val.type == RAVA_VAL_DOUBLE) {
rava_stack_push(stack, rava_value_int((int32_t)val.data.double_val));
} else if (val.type == RAVA_VAL_LONG) {
rava_stack_push(stack, rava_value_int((int32_t)val.data.long_val));
} else {
rava_stack_push(stack, val);
}
break;
case RAVA_TYPE_LONG:
if (val.type == RAVA_VAL_DOUBLE) {
rava_stack_push(stack, rava_value_long((int64_t)val.data.double_val));
} else if (val.type == RAVA_VAL_INT) {
rava_stack_push(stack, rava_value_long((int64_t)rava_value_as_int(val)));
} else {
rava_stack_push(stack, val);
}
break;
case RAVA_TYPE_DOUBLE:
case RAVA_TYPE_FLOAT:
if (val.type == RAVA_VAL_INT) {
rava_stack_push(stack, rava_value_double((double)rava_value_as_int(val)));
} else if (val.type == RAVA_VAL_LONG) {
rava_stack_push(stack, rava_value_double((double)val.data.long_val));
} else {
rava_stack_push(stack, val);
}
break;
default:
rava_stack_push(stack, val);
break;
}
} else {
rava_stack_push(stack, val);
}
break;
}
2025-12-02 06:54:32 +01:00
default:
break;
}
return true;
}
#endif
#ifdef __GNUC__
__attribute__((unused))
static bool _rava_vm_execute_fast(RavaVM_t *vm, RavaCallFrame_t *frame) {
static const void *dispatch_table[] = {
&&op_nop,
&&op_load_const, &&op_load_long, &&op_load_double, &&op_load_string,
2025-12-04 06:14:22 +01:00
&&op_load_local, &&op_load_field, &&op_load_static, &&op_load_array, &&op_load_array_unchecked,
&&op_store_local, &&op_store_field, &&op_store_static, &&op_store_array, &&op_store_array_unchecked,
2025-12-02 06:54:32 +01:00
&&op_add, &&op_sub, &&op_mul, &&op_div, &&op_mod, &&op_neg,
2025-12-02 22:04:26 +01:00
&&op_and, &&op_or, &&op_xor, &&op_not, &&op_bitnot, &&op_shl, &&op_shr, &&op_ushr,
2025-12-02 06:54:32 +01:00
&&op_eq, &&op_ne, &&op_lt, &&op_le, &&op_gt, &&op_ge,
&&op_jump, &&op_jump_if_true, &&op_jump_if_false, &&op_label,
2025-12-04 06:14:22 +01:00
&&op_call, &&op_call_static, &&op_call_recursive, &&op_call_inline, &&op_call_virtual, &&op_call_super, &&op_call_native,
2025-12-02 06:54:32 +01:00
&&op_return, &&op_return_void,
2025-12-03 14:08:55 +01:00
&&op_new, &&op_new_array, &&op_new_array_of_arrays, &&op_array_length, &&op_get_field, &&op_put_field,
2025-12-02 06:54:32 +01:00
&&op_cast, &&op_instanceof,
2025-12-02 21:12:50 +01:00
&&op_throw, &&op_try_begin, &&op_try_end, &&op_pop, &&op_dup,
2025-12-02 06:54:32 +01:00
&&op_print, &&op_println,
&&op_string_length, &&op_string_charat, &&op_string_substring,
&&op_string_equals, &&op_string_compareto,
2025-12-02 21:12:50 +01:00
&&op_string_indexof, &&op_string_contains, &&op_string_startswith,
&&op_string_endswith, &&op_string_tolowercase, &&op_string_touppercase,
&&op_string_trim,
2025-12-02 06:54:32 +01:00
&&op_load_this,
&&op_file_read, &&op_file_write, &&op_file_exists, &&op_file_delete,
&&op_current_time_millis, &&op_nano_time,
&&op_inc_local, &&op_dec_local,
&&op_load_local_const_add, &&op_load_local_const_lt_jumpfalse,
&&op_load_local_const_le_jumpfalse, &&op_load_two_locals,
2025-12-02 21:12:50 +01:00
&&op_add_local_to_local, &&op_load_local_lt_local_jumpfalse,
&&op_math_abs, &&op_math_sqrt, &&op_math_pow, &&op_math_min, &&op_math_max,
&&op_math_floor, &&op_math_ceil, &&op_math_round,
&&op_math_sin, &&op_math_cos, &&op_math_tan, &&op_math_log, &&op_math_exp,
&&op_math_random
2025-12-02 06:54:32 +01:00
};
RavaStack_t *stack = frame->operand_stack;
RavaInstruction_t *instructions = frame->method->instructions->instructions;
size_t instr_count = frame->method->instructions->count;
register size_t pc = frame->pc;
RavaInstruction_t *instr;
if (!frame->method->label_table) {
frame->method->label_table = rava_labeltable_create(frame->method->instructions);
}
LabelTable_t *label_table = (LabelTable_t*)frame->method->label_table;
#define DISPATCH() do { \
if (pc >= instr_count) goto done; \
instr = &instructions[pc++]; \
if (instr->opcode >= sizeof(dispatch_table)/sizeof(dispatch_table[0])) { \
fprintf(stderr, "Invalid opcode: %d at pc=%zu\n", instr->opcode, pc-1); \
vm->had_error = true; \
goto done; \
} \
goto *dispatch_table[instr->opcode]; \
} while(0)
#define DISPATCH_NEXT() do { \
instr = &instructions[pc++]; \
goto *dispatch_table[instr->opcode]; \
} while(0)
DISPATCH();
op_nop:
DISPATCH();
op_load_const:
STACK_PUSH_INT(stack, (int32_t)instr->operand.int_value);
DISPATCH();
op_load_long:
STACK_PUSH_LONG(stack, instr->operand.int_value);
DISPATCH();
op_load_double: {
RavaValue_t v; v.type = RAVA_VAL_DOUBLE; v.data.double_val = instr->operand.float_value;
STACK_PUSH(stack, v);
DISPATCH();
}
op_load_string:
STACK_PUSH(stack, rava_value_string(instr->operand.string_value));
DISPATCH();
op_load_local:
STACK_PUSH(stack, frame->locals[instr->operand.var.index]);
DISPATCH();
op_load_field: {
RavaValue_t obj_val = STACK_POP(stack);
if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) {
STACK_PUSH(stack, rava_object_get_field(obj_val.data.object_val, instr->operand.field.field_name));
} else {
STACK_PUSH_INT(stack, 0);
}
DISPATCH();
}
op_load_static: {
RavaValue_t *field_val = rava_static_field_table_get(vm->static_fields,
instr->operand.field.class_name, instr->operand.field.field_name);
STACK_PUSH(stack, field_val ? *field_val : rava_value_int(0));
DISPATCH();
}
op_load_array: {
RavaValue_t idx = STACK_POP(stack);
RavaValue_t arr = STACK_POP(stack);
if (arr.type == RAVA_VAL_ARRAY && arr.data.array_val) {
2025-12-03 15:01:02 +01:00
int32_t i = rava_value_as_int(idx);
if (i >= 0 && (size_t)i < arr.data.array_val->length) {
size_t index = (size_t)i;
2025-12-02 06:54:32 +01:00
switch (arr.data.array_val->element_type) {
case RAVA_VAL_INT:
STACK_PUSH_INT(stack, ((int32_t*)arr.data.array_val->data)[index]);
break;
case RAVA_VAL_LONG:
STACK_PUSH_LONG(stack, ((int64_t*)arr.data.array_val->data)[index]);
break;
2025-12-03 14:08:55 +01:00
case RAVA_VAL_ARRAY:
case RAVA_VAL_OBJECT:
STACK_PUSH(stack, ((RavaValue_t*)arr.data.array_val->data)[index]);
break;
2025-12-02 06:54:32 +01:00
default:
STACK_PUSH_INT(stack, ((int32_t*)arr.data.array_val->data)[index]);
break;
}
} else {
STACK_PUSH_INT(stack, 0);
}
} else {
STACK_PUSH_INT(stack, 0);
}
DISPATCH();
}
2025-12-04 06:14:22 +01:00
op_load_array_unchecked: {
RavaValue_t idx = STACK_POP(stack);
RavaValue_t arr = STACK_POP(stack);
size_t index = (size_t)rava_value_as_int(idx);
switch (arr.data.array_val->element_type) {
case RAVA_VAL_INT:
STACK_PUSH_INT(stack, ((int32_t*)arr.data.array_val->data)[index]);
break;
case RAVA_VAL_LONG:
STACK_PUSH_LONG(stack, ((int64_t*)arr.data.array_val->data)[index]);
break;
case RAVA_VAL_ARRAY:
case RAVA_VAL_OBJECT:
STACK_PUSH(stack, ((RavaValue_t*)arr.data.array_val->data)[index]);
break;
default:
STACK_PUSH_INT(stack, ((int32_t*)arr.data.array_val->data)[index]);
break;
}
DISPATCH();
}
2025-12-02 06:54:32 +01:00
op_store_local:
frame->locals[instr->operand.var.index] = STACK_POP(stack);
DISPATCH();
op_store_field: {
RavaValue_t value = STACK_POP(stack);
RavaValue_t obj_val = STACK_POP(stack);
if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) {
2025-12-03 17:55:37 +01:00
_rava_object_set_field_vm(obj_val.data.object_val, instr->operand.field.field_name, value, vm);
2025-12-02 06:54:32 +01:00
}
DISPATCH();
}
op_store_static: {
RavaValue_t value = STACK_POP(stack);
rava_static_field_table_set(vm->static_fields, instr->operand.field.class_name,
instr->operand.field.field_name, value);
DISPATCH();
}
op_store_array: {
RavaValue_t value = STACK_POP(stack);
RavaValue_t idx = STACK_POP(stack);
RavaValue_t arr = STACK_POP(stack);
if (arr.type == RAVA_VAL_ARRAY && arr.data.array_val) {
2025-12-03 15:01:02 +01:00
int32_t i = rava_value_as_int(idx);
if (i >= 0 && (size_t)i < arr.data.array_val->length) {
size_t index = (size_t)i;
2025-12-02 06:54:32 +01:00
switch (arr.data.array_val->element_type) {
case RAVA_VAL_INT:
((int32_t*)arr.data.array_val->data)[index] = rava_value_as_int(value);
break;
case RAVA_VAL_LONG:
((int64_t*)arr.data.array_val->data)[index] = rava_value_as_long(value);
break;
2025-12-03 14:08:55 +01:00
case RAVA_VAL_ARRAY:
case RAVA_VAL_OBJECT:
((RavaValue_t*)arr.data.array_val->data)[index] = value;
break;
2025-12-02 06:54:32 +01:00
default:
((int32_t*)arr.data.array_val->data)[index] = rava_value_as_int(value);
break;
}
}
}
DISPATCH();
}
2025-12-04 06:14:22 +01:00
op_store_array_unchecked: {
RavaValue_t value = STACK_POP(stack);
RavaValue_t idx = STACK_POP(stack);
RavaValue_t arr = STACK_POP(stack);
size_t index = (size_t)rava_value_as_int(idx);
switch (arr.data.array_val->element_type) {
case RAVA_VAL_INT:
((int32_t*)arr.data.array_val->data)[index] = rava_value_as_int(value);
break;
case RAVA_VAL_LONG:
((int64_t*)arr.data.array_val->data)[index] = rava_value_as_long(value);
break;
case RAVA_VAL_ARRAY:
case RAVA_VAL_OBJECT:
((RavaValue_t*)arr.data.array_val->data)[index] = value;
break;
default:
((int32_t*)arr.data.array_val->data)[index] = rava_value_as_int(value);
break;
}
DISPATCH();
}
2025-12-02 06:54:32 +01:00
op_add: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) + VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(a.type == RAVA_VAL_STRING || b.type == RAVA_VAL_STRING)) {
2025-12-02 06:54:32 +01:00
char buf_a[64], buf_b[64];
const char *str_a, *str_b;
if (a.type == RAVA_VAL_STRING) str_a = a.data.string_val ? a.data.string_val : "null";
else if (VALUE_IS_LONG(a)) { snprintf(buf_a, 64, "%ld", (long)VALUE_AS_LONG_FAST(a)); str_a = buf_a; }
else { snprintf(buf_a, 64, "%d", rava_value_as_int(a)); str_a = buf_a; }
if (b.type == RAVA_VAL_STRING) str_b = b.data.string_val ? b.data.string_val : "null";
else if (VALUE_IS_LONG(b)) { snprintf(buf_b, 64, "%ld", (long)VALUE_AS_LONG_FAST(b)); str_b = buf_b; }
else { snprintf(buf_b, 64, "%d", rava_value_as_int(b)); str_b = buf_b; }
2025-12-03 17:55:37 +01:00
size_t len_a = strlen(str_a);
size_t len_b = strlen(str_b);
size_t len = len_a + len_b + 1;
bool need_free = (len >= RAVA_STRING_BUFFER_SIZE);
char *result = need_free ? malloc(len) : _rava_get_string_buffer(vm, len);
memcpy(result, str_a, len_a);
memcpy(result + len_a, str_b, len_b + 1);
2025-12-02 06:54:32 +01:00
STACK_PUSH(stack, rava_value_string(result));
2025-12-03 17:55:37 +01:00
if (need_free) free(result);
2025-12-02 06:54:32 +01:00
} else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) {
STACK_PUSH_LONG(stack, rava_value_as_long(a) + rava_value_as_long(b));
} else {
STACK_PUSH_INT(stack, rava_value_as_int(a) + rava_value_as_int(b));
}
DISPATCH();
}
op_sub: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) - VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_LONG(stack, rava_value_as_long(a) - rava_value_as_long(b));
} else {
STACK_PUSH_INT(stack, rava_value_as_int(a) - rava_value_as_int(b));
}
DISPATCH();
}
op_mul: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) * VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_LONG(stack, rava_value_as_long(a) * rava_value_as_long(b));
} else {
STACK_PUSH_INT(stack, rava_value_as_int(a) * rava_value_as_int(b));
}
DISPATCH();
}
op_div: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
int32_t divisor = VALUE_AS_INT_FAST(b);
2025-12-03 17:55:37 +01:00
if (UNLIKELY(divisor == 0)) { vm->had_error = true; goto done; }
2025-12-02 06:54:32 +01:00
STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) / divisor);
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
int64_t divisor = rava_value_as_long(b);
if (divisor == 0) { vm->had_error = true; goto done; }
STACK_PUSH_LONG(stack, rava_value_as_long(a) / divisor);
} else {
int32_t divisor = rava_value_as_int(b);
if (divisor == 0) { vm->had_error = true; goto done; }
STACK_PUSH_INT(stack, rava_value_as_int(a) / divisor);
}
DISPATCH();
}
op_mod: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
int32_t divisor = VALUE_AS_INT_FAST(b);
2025-12-03 17:55:37 +01:00
if (UNLIKELY(divisor == 0)) { vm->had_error = true; goto done; }
2025-12-02 06:54:32 +01:00
STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) % divisor);
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
int64_t divisor = rava_value_as_long(b);
if (divisor == 0) { vm->had_error = true; goto done; }
STACK_PUSH_LONG(stack, rava_value_as_long(a) % divisor);
} else {
int32_t divisor = rava_value_as_int(b);
if (divisor == 0) { vm->had_error = true; goto done; }
STACK_PUSH_INT(stack, rava_value_as_int(a) % divisor);
}
DISPATCH();
}
op_neg: {
RavaValue_t a = STACK_POP(stack);
if (VALUE_IS_INT(a)) {
STACK_PUSH_INT(stack, -VALUE_AS_INT_FAST(a));
} else if (VALUE_IS_LONG(a)) {
STACK_PUSH_LONG(stack, -VALUE_AS_LONG_FAST(a));
} else {
STACK_PUSH_INT(stack, -rava_value_as_int(a));
}
DISPATCH();
}
op_and: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
STACK_PUSH_INT(stack, rava_value_as_int(a) & rava_value_as_int(b));
DISPATCH();
}
op_or: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
STACK_PUSH_INT(stack, rava_value_as_int(a) | rava_value_as_int(b));
DISPATCH();
}
op_xor: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
STACK_PUSH_INT(stack, rava_value_as_int(a) ^ rava_value_as_int(b));
DISPATCH();
}
op_not: {
RavaValue_t a = STACK_POP(stack);
STACK_PUSH_BOOL(stack, !rava_value_as_boolean(a));
DISPATCH();
}
2025-12-02 22:04:26 +01:00
op_bitnot: {
RavaValue_t a = STACK_POP(stack);
STACK_PUSH(stack, rava_value_int(~rava_value_as_int(a)));
DISPATCH();
}
2025-12-02 06:54:32 +01:00
op_shl: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
STACK_PUSH_INT(stack, rava_value_as_int(a) << rava_value_as_int(b));
DISPATCH();
}
op_shr: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
STACK_PUSH_INT(stack, rava_value_as_int(a) >> rava_value_as_int(b));
DISPATCH();
}
op_ushr: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
STACK_PUSH_INT(stack, (uint32_t)rava_value_as_int(a) >> rava_value_as_int(b));
DISPATCH();
}
op_eq: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) == VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, rava_value_as_long(a) == rava_value_as_long(b));
} else {
STACK_PUSH_BOOL(stack, rava_value_as_int(a) == rava_value_as_int(b));
}
DISPATCH();
}
op_ne: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) != VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, rava_value_as_long(a) != rava_value_as_long(b));
} else {
STACK_PUSH_BOOL(stack, rava_value_as_int(a) != rava_value_as_int(b));
}
DISPATCH();
}
op_lt: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) < VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, rava_value_as_long(a) < rava_value_as_long(b));
} else {
STACK_PUSH_BOOL(stack, rava_value_as_int(a) < rava_value_as_int(b));
}
DISPATCH();
}
op_le: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) <= VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, rava_value_as_long(a) <= rava_value_as_long(b));
} else {
STACK_PUSH_BOOL(stack, rava_value_as_int(a) <= rava_value_as_int(b));
}
DISPATCH();
}
op_gt: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) > VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, rava_value_as_long(a) > rava_value_as_long(b));
} else {
STACK_PUSH_BOOL(stack, rava_value_as_int(a) > rava_value_as_int(b));
}
DISPATCH();
}
op_ge: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
2025-12-03 17:55:37 +01:00
if (LIKELY(VALUE_IS_INT(a) && VALUE_IS_INT(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) >= VALUE_AS_INT_FAST(b));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(VALUE_IS_LONG(a) || VALUE_IS_LONG(b))) {
2025-12-02 06:54:32 +01:00
STACK_PUSH_BOOL(stack, rava_value_as_long(a) >= rava_value_as_long(b));
} else {
STACK_PUSH_BOOL(stack, rava_value_as_int(a) >= rava_value_as_int(b));
}
DISPATCH();
}
op_jump: {
pc = rava_labeltable_lookup(label_table, instr->operand.label_id);
DISPATCH();
}
op_jump_if_true: {
RavaValue_t cond = STACK_POP(stack);
if (rava_value_as_boolean(cond)) {
pc = rava_labeltable_lookup(label_table, instr->operand.label_id);
}
DISPATCH();
}
op_jump_if_false: {
RavaValue_t cond = STACK_POP(stack);
if (!rava_value_as_boolean(cond)) {
pc = rava_labeltable_lookup(label_table, instr->operand.label_id);
}
DISPATCH();
}
op_label:
DISPATCH();
op_call:
op_call_static: {
frame->pc = pc;
RavaMethod_t *target_method = _rava_vm_find_method_cached(vm,
instr->operand.call.class_name, instr->operand.call.method_name);
if (!target_method) {
vm->had_error = true;
2025-12-04 06:14:22 +01:00
vm->error_message = malloc(RAVA_ERROR_BUFFER_SIZE);
snprintf(vm->error_message, RAVA_ERROR_BUFFER_SIZE, "Method not found: %s.%s",
2025-12-02 06:54:32 +01:00
instr->operand.call.class_name, instr->operand.call.method_name);
goto done;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(target_method);
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
new_frame->locals[i] = STACK_POP(stack);
}
rava_call_stack_push(vm->call_stack, new_frame);
if (!_rava_vm_execute_fast(vm, new_frame)) {
goto done;
}
if (!rava_stack_is_empty(new_frame->operand_stack)) {
STACK_PUSH(stack, rava_stack_pop(new_frame->operand_stack));
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
DISPATCH();
}
2025-12-03 23:51:38 +01:00
op_call_recursive: {
frame->pc = pc;
RavaMethod_t *target_method = (RavaMethod_t*)instr->operand.call.cached_method;
if (UNLIKELY(!target_method)) {
target_method = frame->method;
instr->operand.call.cached_method = target_method;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(target_method);
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
new_frame->locals[i] = STACK_POP(stack);
}
rava_call_stack_push(vm->call_stack, new_frame);
if (!_rava_vm_execute_fast(vm, new_frame)) {
goto done;
}
if (!rava_stack_is_empty(new_frame->operand_stack)) {
STACK_PUSH(stack, rava_stack_pop(new_frame->operand_stack));
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
DISPATCH();
}
2025-12-04 06:14:22 +01:00
op_call_inline: {
frame->pc = pc;
RavaMethod_t *target_method = (RavaMethod_t*)instr->operand.call.cached_method;
if (UNLIKELY(!target_method)) {
goto done;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(target_method);
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
new_frame->locals[i] = STACK_POP(stack);
}
rava_call_stack_push(vm->call_stack, new_frame);
if (!_rava_vm_execute_fast(vm, new_frame)) {
goto done;
}
if (!rava_stack_is_empty(new_frame->operand_stack)) {
STACK_PUSH(stack, rava_stack_pop(new_frame->operand_stack));
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
DISPATCH();
}
2025-12-02 06:54:32 +01:00
op_call_virtual: {
frame->pc = pc;
RavaValue_t this_val = stack->values[stack->top - instr->operand.call.arg_count - 1];
const char *target_class = instr->operand.call.class_name;
if (this_val.type == RAVA_VAL_OBJECT && this_val.data.object_val) {
target_class = this_val.data.object_val->class_name;
}
RavaMethod_t *target_method = _rava_vm_find_method_cached(vm, target_class, instr->operand.call.method_name);
if (!target_method) {
if (strcmp(instr->operand.call.method_name, "<init>") == 0 && instr->operand.call.arg_count == 0) {
STACK_POP(stack);
DISPATCH();
}
vm->had_error = true;
2025-12-04 06:14:22 +01:00
vm->error_message = malloc(RAVA_ERROR_BUFFER_SIZE);
snprintf(vm->error_message, RAVA_ERROR_BUFFER_SIZE, "Method not found: %s.%s",
2025-12-02 06:54:32 +01:00
target_class, instr->operand.call.method_name);
goto done;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(target_method);
new_frame->has_this = true;
RavaValue_t args[16];
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
args[i] = STACK_POP(stack);
}
new_frame->this_ref = STACK_POP(stack);
for (int i = 0; i < instr->operand.call.arg_count; i++) {
new_frame->locals[i] = args[i];
}
rava_call_stack_push(vm->call_stack, new_frame);
if (!_rava_vm_execute_fast(vm, new_frame)) {
goto done;
}
if (!rava_stack_is_empty(new_frame->operand_stack)) {
STACK_PUSH(stack, rava_stack_pop(new_frame->operand_stack));
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
DISPATCH();
}
2025-12-02 21:12:50 +01:00
op_call_super: {
frame->pc = pc;
const char *super_class = instr->operand.call.class_name;
RavaMethod_t *target_method = _rava_vm_find_method_cached(vm, super_class, instr->operand.call.method_name);
if (!target_method) {
vm->had_error = true;
2025-12-04 06:14:22 +01:00
vm->error_message = malloc(RAVA_ERROR_BUFFER_SIZE);
snprintf(vm->error_message, RAVA_ERROR_BUFFER_SIZE, "Super method not found: %s.%s",
2025-12-02 21:12:50 +01:00
super_class, instr->operand.call.method_name);
goto done;
}
RavaCallFrame_t *new_frame = rava_call_frame_create(target_method);
new_frame->has_this = true;
RavaValue_t args[16];
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
args[i] = STACK_POP(stack);
}
new_frame->this_ref = STACK_POP(stack);
for (int i = 0; i < instr->operand.call.arg_count; i++) {
new_frame->locals[i] = args[i];
}
rava_call_stack_push(vm->call_stack, new_frame);
if (!_rava_vm_execute_fast(vm, new_frame)) {
goto done;
}
if (!rava_stack_is_empty(new_frame->operand_stack)) {
STACK_PUSH(stack, rava_stack_pop(new_frame->operand_stack));
}
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(new_frame);
DISPATCH();
}
2025-12-02 06:54:32 +01:00
op_call_native:
DISPATCH();
op_return:
goto done;
op_return_void:
goto done;
op_new: {
RavaObject_t *obj = rava_object_create(instr->operand.call.class_name);
STACK_PUSH(stack, rava_value_object(obj));
DISPATCH();
}
op_new_array: {
RavaValue_t size_val = STACK_POP(stack);
size_t size = (size_t)rava_value_as_int(size_val);
RavaArray_t *array = rava_array_create(RAVA_VAL_INT, size);
STACK_PUSH(stack, rava_value_array(array));
DISPATCH();
}
2025-12-03 14:08:55 +01:00
op_new_array_of_arrays: {
RavaValue_t size_val = STACK_POP(stack);
size_t size = (size_t)rava_value_as_int(size_val);
RavaArray_t *array = rava_array_create(RAVA_VAL_ARRAY, size);
STACK_PUSH(stack, rava_value_array(array));
DISPATCH();
}
2025-12-02 06:54:32 +01:00
op_array_length: {
RavaValue_t arr = STACK_POP(stack);
if (arr.type == RAVA_VAL_ARRAY && arr.data.array_val) {
STACK_PUSH_INT(stack, (int32_t)arr.data.array_val->length);
} else {
STACK_PUSH_INT(stack, 0);
}
DISPATCH();
}
op_get_field: {
RavaValue_t obj_val = STACK_POP(stack);
if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) {
STACK_PUSH(stack, rava_object_get_field(obj_val.data.object_val, instr->operand.field.field_name));
} else {
STACK_PUSH_INT(stack, 0);
}
DISPATCH();
}
op_put_field: {
RavaValue_t value = STACK_POP(stack);
RavaValue_t obj_val = STACK_POP(stack);
if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) {
2025-12-03 17:55:37 +01:00
_rava_object_set_field_vm(obj_val.data.object_val, instr->operand.field.field_name, value, vm);
2025-12-02 06:54:32 +01:00
}
DISPATCH();
}
2025-12-02 21:12:50 +01:00
op_cast: {
RavaValue_t val = STACK_POP(stack);
RavaType_t *target_type = instr->operand.var.type;
if (target_type) {
switch (target_type->kind) {
case RAVA_TYPE_INT:
if (val.type == RAVA_VAL_DOUBLE) {
STACK_PUSH_INT(stack, (int32_t)val.data.double_val);
} else if (val.type == RAVA_VAL_LONG) {
STACK_PUSH_INT(stack, (int32_t)val.data.long_val);
} else {
STACK_PUSH(stack, val);
}
break;
case RAVA_TYPE_LONG:
if (val.type == RAVA_VAL_DOUBLE) {
STACK_PUSH_LONG(stack, (int64_t)val.data.double_val);
} else if (val.type == RAVA_VAL_INT) {
STACK_PUSH_LONG(stack, (int64_t)rava_value_as_int(val));
} else {
STACK_PUSH(stack, val);
}
break;
case RAVA_TYPE_DOUBLE: {
RavaValue_t r; r.type = RAVA_VAL_DOUBLE;
if (val.type == RAVA_VAL_INT) {
r.data.double_val = (double)rava_value_as_int(val);
} else if (val.type == RAVA_VAL_LONG) {
r.data.double_val = (double)val.data.long_val;
} else {
r.data.double_val = val.data.double_val;
}
STACK_PUSH(stack, r);
break;
}
case RAVA_TYPE_FLOAT: {
RavaValue_t r; r.type = RAVA_VAL_DOUBLE;
if (val.type == RAVA_VAL_INT) {
r.data.double_val = (double)rava_value_as_int(val);
} else if (val.type == RAVA_VAL_LONG) {
r.data.double_val = (double)val.data.long_val;
} else {
r.data.double_val = val.data.double_val;
}
STACK_PUSH(stack, r);
break;
}
default:
STACK_PUSH(stack, val);
break;
}
} else {
STACK_PUSH(stack, val);
}
2025-12-02 06:54:32 +01:00
DISPATCH();
2025-12-02 21:12:50 +01:00
}
2025-12-02 06:54:32 +01:00
2025-12-02 21:12:50 +01:00
op_instanceof: {
RavaValue_t val = STACK_POP(stack);
bool result = false;
if (val.type == RAVA_VAL_OBJECT && val.data.object_val) {
result = strcmp(val.data.object_val->class_name, instr->operand.string_value) == 0;
}
STACK_PUSH(stack, rava_value_boolean(result));
2025-12-02 06:54:32 +01:00
DISPATCH();
}
2025-12-02 21:12:50 +01:00
op_throw: {
RavaValue_t exception = STACK_POP(stack);
vm->has_exception = true;
vm->exception_value = exception;
RavaExceptionHandler_t handler;
if (rava_exception_stack_pop(vm->exception_stack, &handler)) {
frame->locals[handler.exception_local] = exception;
pc = rava_labeltable_lookup(label_table, handler.catch_label);
vm->has_exception = false;
DISPATCH();
} else {
vm->had_error = true;
goto done;
}
}
op_try_begin: {
RavaExceptionHandler_t handler;
handler.catch_label = instr->operand.try_handler.catch_label;
handler.finally_label = instr->operand.try_handler.finally_label;
handler.end_label = instr->operand.try_handler.end_label;
handler.exception_local = instr->operand.try_handler.exception_local;
handler.stack_depth = stack->top;
rava_exception_stack_push(vm->exception_stack, handler);
DISPATCH();
}
op_try_end: {
RavaExceptionHandler_t handler;
rava_exception_stack_pop(vm->exception_stack, &handler);
DISPATCH();
}
op_pop:
(void)STACK_POP(stack);
DISPATCH();
op_dup: {
RavaValue_t top = STACK_PEEK(stack);
STACK_PUSH(stack, top);
DISPATCH();
}
op_print: {
RavaValue_t value = STACK_POP(stack);
switch (value.type) {
2025-12-02 06:54:32 +01:00
case RAVA_VAL_STRING: printf("%s", value.data.string_val ? value.data.string_val : "null"); break;
case RAVA_VAL_BOOLEAN: printf("%s", value.data.bool_val ? "true" : "false"); break;
case RAVA_VAL_DOUBLE: printf("%g", value.data.double_val); break;
case RAVA_VAL_LONG: printf("%ld", (long)value.data.long_val); break;
case RAVA_VAL_NULL: printf("null"); break;
default: printf("%d", rava_value_as_int(value)); break;
}
fflush(stdout);
DISPATCH();
}
op_println: {
RavaValue_t value = STACK_POP(stack);
switch (value.type) {
case RAVA_VAL_STRING: printf("%s\n", value.data.string_val ? value.data.string_val : "null"); break;
case RAVA_VAL_BOOLEAN: printf("%s\n", value.data.bool_val ? "true" : "false"); break;
case RAVA_VAL_DOUBLE: printf("%g\n", value.data.double_val); break;
case RAVA_VAL_LONG: printf("%ld\n", (long)value.data.long_val); break;
case RAVA_VAL_NULL: printf("null\n"); break;
default: printf("%d\n", rava_value_as_int(value)); break;
}
DISPATCH();
}
op_string_length: {
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val) {
STACK_PUSH_INT(stack, (int32_t)strlen(str.data.string_val));
} else {
STACK_PUSH_INT(stack, 0);
}
DISPATCH();
}
op_string_charat: {
RavaValue_t idx = STACK_POP(stack);
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val) {
size_t index = (size_t)rava_value_as_int(idx);
if (index < strlen(str.data.string_val)) {
RavaValue_t v; v.type = RAVA_VAL_CHAR; v.data.char_val = str.data.string_val[index];
STACK_PUSH(stack, v);
} else {
RavaValue_t v; v.type = RAVA_VAL_CHAR; v.data.char_val = 0;
STACK_PUSH(stack, v);
}
} else {
RavaValue_t v; v.type = RAVA_VAL_CHAR; v.data.char_val = 0;
STACK_PUSH(stack, v);
}
DISPATCH();
}
op_string_substring: {
RavaValue_t end_val = STACK_POP(stack);
RavaValue_t start_val = STACK_POP(stack);
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val) {
size_t len = strlen(str.data.string_val);
size_t start = (size_t)rava_value_as_int(start_val);
size_t end = (size_t)rava_value_as_int(end_val);
if (start > len) start = len;
if (end > len) end = len;
if (start > end) start = end;
size_t sub_len = end - start;
char *sub = malloc(sub_len + 1);
strncpy(sub, str.data.string_val + start, sub_len);
sub[sub_len] = '\0';
STACK_PUSH(stack, rava_value_string(sub));
free(sub);
} else {
STACK_PUSH(stack, rava_value_string(""));
}
DISPATCH();
}
op_string_equals: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING) {
if (a.data.string_val && b.data.string_val) {
STACK_PUSH_BOOL(stack, strcmp(a.data.string_val, b.data.string_val) == 0);
} else {
STACK_PUSH_BOOL(stack, a.data.string_val == b.data.string_val);
}
} else {
STACK_PUSH_BOOL(stack, false);
}
DISPATCH();
}
op_string_compareto: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING &&
a.data.string_val && b.data.string_val) {
STACK_PUSH_INT(stack, strcmp(a.data.string_val, b.data.string_val));
} else {
STACK_PUSH_INT(stack, 0);
}
DISPATCH();
}
2025-12-02 21:12:50 +01:00
op_string_indexof: {
RavaValue_t needle = STACK_POP(stack);
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val &&
needle.type == RAVA_VAL_STRING && needle.data.string_val) {
const char *found = strstr(str.data.string_val, needle.data.string_val);
STACK_PUSH_INT(stack, found ? (int32_t)(found - str.data.string_val) : -1);
} else {
STACK_PUSH_INT(stack, -1);
}
DISPATCH();
}
op_string_contains: {
RavaValue_t needle = STACK_POP(stack);
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val &&
needle.type == RAVA_VAL_STRING && needle.data.string_val) {
STACK_PUSH_BOOL(stack, strstr(str.data.string_val, needle.data.string_val) != NULL);
} else {
STACK_PUSH_BOOL(stack, false);
}
DISPATCH();
}
op_string_startswith: {
RavaValue_t prefix = STACK_POP(stack);
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val &&
prefix.type == RAVA_VAL_STRING && prefix.data.string_val) {
size_t plen = strlen(prefix.data.string_val);
STACK_PUSH_BOOL(stack, strncmp(str.data.string_val, prefix.data.string_val, plen) == 0);
} else {
STACK_PUSH_BOOL(stack, false);
}
DISPATCH();
}
op_string_endswith: {
RavaValue_t suffix = STACK_POP(stack);
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val &&
suffix.type == RAVA_VAL_STRING && suffix.data.string_val) {
size_t slen = strlen(str.data.string_val);
size_t suflen = strlen(suffix.data.string_val);
if (suflen <= slen) {
STACK_PUSH_BOOL(stack, strcmp(str.data.string_val + slen - suflen, suffix.data.string_val) == 0);
} else {
STACK_PUSH_BOOL(stack, false);
}
} else {
STACK_PUSH_BOOL(stack, false);
}
DISPATCH();
}
op_string_tolowercase: {
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val) {
size_t len = strlen(str.data.string_val);
char *lower = malloc(len + 1);
for (size_t i = 0; i <= len; i++) {
lower[i] = (char)tolower((unsigned char)str.data.string_val[i]);
}
STACK_PUSH(stack, rava_value_string(lower));
} else {
STACK_PUSH(stack, rava_value_string(""));
}
DISPATCH();
}
op_string_touppercase: {
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val) {
size_t len = strlen(str.data.string_val);
char *upper = malloc(len + 1);
for (size_t i = 0; i <= len; i++) {
upper[i] = (char)toupper((unsigned char)str.data.string_val[i]);
}
STACK_PUSH(stack, rava_value_string(upper));
} else {
STACK_PUSH(stack, rava_value_string(""));
}
DISPATCH();
}
op_string_trim: {
RavaValue_t str = STACK_POP(stack);
if (str.type == RAVA_VAL_STRING && str.data.string_val) {
const char *start = str.data.string_val;
while (*start && isspace((unsigned char)*start)) start++;
const char *end = str.data.string_val + strlen(str.data.string_val);
while (end > start && isspace((unsigned char)*(end - 1))) end--;
size_t len = (size_t)(end - start);
char *trimmed = malloc(len + 1);
strncpy(trimmed, start, len);
trimmed[len] = '\0';
STACK_PUSH(stack, rava_value_string(trimmed));
} else {
STACK_PUSH(stack, rava_value_string(""));
}
DISPATCH();
}
2025-12-02 06:54:32 +01:00
op_load_this:
if (frame->has_this) {
STACK_PUSH(stack, frame->this_ref);
} else {
STACK_PUSH(stack, rava_value_null());
}
DISPATCH();
op_file_read: {
RavaValue_t path_val = STACK_POP(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) {
FILE *file = fopen(path_val.data.string_val, "r");
if (file) {
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char *content = malloc(size + 1);
if (content) {
size_t rd = fread(content, 1, size, file);
content[rd] = '\0';
STACK_PUSH(stack, rava_value_string(content));
free(content);
} else {
STACK_PUSH(stack, rava_value_null());
}
fclose(file);
} else {
STACK_PUSH(stack, rava_value_null());
}
} else {
STACK_PUSH(stack, rava_value_null());
}
DISPATCH();
}
op_file_write: {
RavaValue_t content_val = STACK_POP(stack);
RavaValue_t path_val = STACK_POP(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val &&
content_val.type == RAVA_VAL_STRING && content_val.data.string_val) {
FILE *file = fopen(path_val.data.string_val, "w");
if (file) {
fputs(content_val.data.string_val, file);
fclose(file);
STACK_PUSH_BOOL(stack, true);
} else {
STACK_PUSH_BOOL(stack, false);
}
} else {
STACK_PUSH_BOOL(stack, false);
}
DISPATCH();
}
op_file_exists: {
RavaValue_t path_val = STACK_POP(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) {
FILE *file = fopen(path_val.data.string_val, "r");
if (file) {
fclose(file);
STACK_PUSH_BOOL(stack, true);
} else {
STACK_PUSH_BOOL(stack, false);
}
} else {
STACK_PUSH_BOOL(stack, false);
}
DISPATCH();
}
op_file_delete: {
RavaValue_t path_val = STACK_POP(stack);
if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) {
int result = remove(path_val.data.string_val);
STACK_PUSH_BOOL(stack, result == 0);
} else {
STACK_PUSH_BOOL(stack, false);
}
DISPATCH();
}
op_current_time_millis: {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
int64_t millis = (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
STACK_PUSH_LONG(stack, millis);
DISPATCH();
}
op_nano_time: {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int64_t nanos = (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec;
STACK_PUSH_LONG(stack, nanos);
DISPATCH();
}
op_inc_local: {
size_t idx = (size_t)instr->operand.var.index;
if (VALUE_IS_INT(frame->locals[idx])) {
frame->locals[idx].data.int_val++;
} else {
frame->locals[idx].data.long_val++;
}
DISPATCH();
}
op_dec_local: {
size_t idx = (size_t)instr->operand.var.index;
if (VALUE_IS_INT(frame->locals[idx])) {
frame->locals[idx].data.int_val--;
} else {
frame->locals[idx].data.long_val--;
}
DISPATCH();
}
op_load_local_const_add: {
size_t idx = (size_t)instr->operand.super.local_index;
int64_t c = instr->operand.super.const_value;
RavaValue_t v = frame->locals[idx];
if (VALUE_IS_INT(v)) {
STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(v) + (int32_t)c);
} else {
STACK_PUSH_LONG(stack, rava_value_as_long(v) + c);
}
DISPATCH();
}
op_load_local_const_lt_jumpfalse: {
size_t idx = (size_t)instr->operand.super.local_index;
int64_t c = instr->operand.super.const_value;
int label = instr->operand.super.label_id;
int64_t local_val = rava_value_as_long(frame->locals[idx]);
if (!(local_val < c)) {
pc = rava_labeltable_lookup(label_table, label);
}
DISPATCH();
}
op_load_local_const_le_jumpfalse: {
size_t idx = (size_t)instr->operand.super.local_index;
int64_t c = instr->operand.super.const_value;
int label = instr->operand.super.label_id;
int64_t local_val = rava_value_as_long(frame->locals[idx]);
if (!(local_val <= c)) {
pc = rava_labeltable_lookup(label_table, label);
}
DISPATCH();
}
op_load_two_locals: {
size_t idx1 = (size_t)instr->operand.two_locals.index1;
size_t idx2 = (size_t)instr->operand.two_locals.index2;
STACK_PUSH(stack, frame->locals[idx1]);
STACK_PUSH(stack, frame->locals[idx2]);
DISPATCH();
}
op_add_local_to_local: {
size_t dest = (size_t)instr->operand.add_locals.dest_index;
size_t src = (size_t)instr->operand.add_locals.src_index;
if (VALUE_IS_INT(frame->locals[dest]) && VALUE_IS_INT(frame->locals[src])) {
frame->locals[dest].data.int_val += frame->locals[src].data.int_val;
} else {
frame->locals[dest].data.long_val =
rava_value_as_long(frame->locals[dest]) + rava_value_as_long(frame->locals[src]);
frame->locals[dest].type = RAVA_VAL_LONG;
}
DISPATCH();
}
op_load_local_lt_local_jumpfalse: {
size_t local1 = (size_t)instr->operand.cmp_locals.local1;
size_t local2 = (size_t)instr->operand.cmp_locals.local2;
int label = instr->operand.cmp_locals.label_id;
int64_t v1 = rava_value_as_long(frame->locals[local1]);
int64_t v2 = rava_value_as_long(frame->locals[local2]);
if (!(v1 < v2)) {
pc = rava_labeltable_lookup(label_table, label);
}
DISPATCH();
}
2025-12-02 21:12:50 +01:00
op_math_abs: {
RavaValue_t val = STACK_POP(stack);
if (val.type == RAVA_VAL_DOUBLE) {
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = fabs(val.data.double_val);
STACK_PUSH(stack, r);
} else if (val.type == RAVA_VAL_LONG) {
int64_t v = val.data.long_val;
STACK_PUSH_LONG(stack, v < 0 ? -v : v);
} else {
int32_t v = rava_value_as_int(val);
STACK_PUSH_INT(stack, v < 0 ? -v : v);
}
DISPATCH();
}
op_math_sqrt: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = sqrt(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_pow: {
RavaValue_t exp = STACK_POP(stack);
RavaValue_t base = STACK_POP(stack);
double b = (base.type == RAVA_VAL_DOUBLE) ? base.data.double_val : (double)rava_value_as_int(base);
double e = (exp.type == RAVA_VAL_DOUBLE) ? exp.data.double_val : (double)rava_value_as_int(exp);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = pow(b, e);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_min: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
if (a.type == RAVA_VAL_DOUBLE || b.type == RAVA_VAL_DOUBLE) {
double da = (a.type == RAVA_VAL_DOUBLE) ? a.data.double_val : (double)rava_value_as_int(a);
double db = (b.type == RAVA_VAL_DOUBLE) ? b.data.double_val : (double)rava_value_as_int(b);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = da < db ? da : db;
STACK_PUSH(stack, r);
} else {
int32_t ia = rava_value_as_int(a);
int32_t ib = rava_value_as_int(b);
STACK_PUSH_INT(stack, ia < ib ? ia : ib);
}
DISPATCH();
}
op_math_max: {
RavaValue_t b = STACK_POP(stack);
RavaValue_t a = STACK_POP(stack);
if (a.type == RAVA_VAL_DOUBLE || b.type == RAVA_VAL_DOUBLE) {
double da = (a.type == RAVA_VAL_DOUBLE) ? a.data.double_val : (double)rava_value_as_int(a);
double db = (b.type == RAVA_VAL_DOUBLE) ? b.data.double_val : (double)rava_value_as_int(b);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = da > db ? da : db;
STACK_PUSH(stack, r);
} else {
int32_t ia = rava_value_as_int(a);
int32_t ib = rava_value_as_int(b);
STACK_PUSH_INT(stack, ia > ib ? ia : ib);
}
DISPATCH();
}
op_math_floor: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = floor(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_ceil: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = ceil(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_round: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
STACK_PUSH_LONG(stack, (int64_t)round(d));
DISPATCH();
}
op_math_sin: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = sin(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_cos: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = cos(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_tan: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = tan(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_log: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = log(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_exp: {
RavaValue_t val = STACK_POP(stack);
double d = (val.type == RAVA_VAL_DOUBLE) ? val.data.double_val : (double)rava_value_as_int(val);
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = exp(d);
STACK_PUSH(stack, r);
DISPATCH();
}
op_math_random: {
RavaValue_t r; r.type = RAVA_VAL_DOUBLE; r.data.double_val = (double)rand() / (double)RAND_MAX;
STACK_PUSH(stack, r);
DISPATCH();
}
2025-12-02 06:54:32 +01:00
done:
frame->pc = pc;
return !vm->had_error;
#undef DISPATCH
#undef DISPATCH_NEXT
}
static bool _rava_vm_execute_ultrafast(RavaVM_t *vm, RavaMethod_t *entry_method) {
static const void *dispatch_table[] = {
&&uf_nop,
&&uf_load_const, &&uf_load_long, &&uf_load_double, &&uf_load_string,
2025-12-04 06:14:22 +01:00
&&uf_load_local, &&uf_load_field, &&uf_load_static, &&uf_load_array, &&uf_load_array_unchecked,
&&uf_store_local, &&uf_store_field, &&uf_store_static, &&uf_store_array, &&uf_store_array_unchecked,
2025-12-02 06:54:32 +01:00
&&uf_add, &&uf_sub, &&uf_mul, &&uf_div, &&uf_mod, &&uf_neg,
2025-12-02 22:04:26 +01:00
&&uf_and, &&uf_or, &&uf_xor, &&uf_not, &&uf_bitnot, &&uf_shl, &&uf_shr, &&uf_ushr,
2025-12-02 06:54:32 +01:00
&&uf_eq, &&uf_ne, &&uf_lt, &&uf_le, &&uf_gt, &&uf_ge,
&&uf_jump, &&uf_jump_if_true, &&uf_jump_if_false, &&uf_label,
2025-12-04 06:14:22 +01:00
&&uf_call, &&uf_call_static, &&uf_call_recursive, &&uf_call_inline, &&uf_call_virtual, &&uf_call_super, &&uf_call_native,
2025-12-02 06:54:32 +01:00
&&uf_return, &&uf_return_void,
2025-12-03 14:08:55 +01:00
&&uf_new, &&uf_new_array, &&uf_new_array_of_arrays, &&uf_array_length, &&uf_get_field, &&uf_put_field,
2025-12-02 06:54:32 +01:00
&&uf_cast, &&uf_instanceof,
2025-12-02 21:12:50 +01:00
&&uf_throw, &&uf_try_begin, &&uf_try_end, &&uf_pop, &&uf_dup,
2025-12-02 06:54:32 +01:00
&&uf_print, &&uf_println,
&&uf_string_length, &&uf_string_charat, &&uf_string_substring,
&&uf_string_equals, &&uf_string_compareto,
2025-12-02 21:12:50 +01:00
&&uf_string_indexof, &&uf_string_contains, &&uf_string_startswith,
&&uf_string_endswith, &&uf_string_tolowercase, &&uf_string_touppercase,
&&uf_string_trim,
2025-12-02 06:54:32 +01:00
&&uf_load_this,
&&uf_file_read, &&uf_file_write, &&uf_file_exists, &&uf_file_delete,
&&uf_current_time_millis, &&uf_nano_time,
&&uf_inc_local, &&uf_dec_local,
&&uf_load_local_const_add, &&uf_load_local_const_lt_jumpfalse,
&&uf_load_local_const_le_jumpfalse, &&uf_load_two_locals,
2025-12-02 21:12:50 +01:00
&&uf_add_local_to_local, &&uf_load_local_lt_local_jumpfalse,
&&uf_math_abs, &&uf_math_sqrt, &&uf_math_pow, &&uf_math_min, &&uf_math_max,
&&uf_math_floor, &&uf_math_ceil, &&uf_math_round,
&&uf_math_sin, &&uf_math_cos, &&uf_math_tan, &&uf_math_log, &&uf_math_exp,
2025-12-04 16:49:20 +01:00
&&uf_math_random,
&&uf_arraylist_new, &&uf_arraylist_add, &&uf_arraylist_get, &&uf_arraylist_set,
&&uf_arraylist_size, &&uf_arraylist_remove, &&uf_arraylist_clear, &&uf_arraylist_isempty,
&&uf_hashmap_new, &&uf_hashmap_put, &&uf_hashmap_get, &&uf_hashmap_remove,
&&uf_hashmap_size, &&uf_hashmap_containskey, &&uf_hashmap_clear
2025-12-02 06:54:32 +01:00
};
rava_fastframe_reset();
FastFrame_t *frame = rava_fastframe_push(entry_method, entry_method->local_count);
if (!frame) return false;
if (!entry_method->label_table) {
rava_optimize_superinstructions(entry_method);
entry_method->label_table = rava_labeltable_create(entry_method->instructions);
}
RavaInstruction_t *instructions = entry_method->instructions->instructions;
size_t instr_count = entry_method->instructions->count;
register size_t pc = 0;
RavaInstruction_t *instr;
RavaNanboxValue_t result = rava_nanbox_null();
#define UF_DISPATCH() do { \
if (pc >= instr_count) goto uf_done; \
instr = &instructions[pc++]; \
goto *dispatch_table[instr->opcode]; \
} while(0)
#define UF_PUSH(v) rava_fastframe_stack_push(frame, v)
#define UF_POP() rava_fastframe_stack_pop(frame)
#define UF_PEEK() rava_fastframe_stack_peek(frame)
UF_DISPATCH();
uf_nop:
UF_DISPATCH();
uf_load_const:
UF_PUSH(rava_nanbox_int((int32_t)instr->operand.int_value));
UF_DISPATCH();
uf_load_long:
UF_PUSH(rava_nanbox_long(instr->operand.int_value));
UF_DISPATCH();
uf_load_double:
UF_PUSH(rava_nanbox_double(instr->operand.float_value));
UF_DISPATCH();
uf_load_string:
UF_PUSH(rava_nanbox_string(instr->operand.string_value));
UF_DISPATCH();
uf_load_local:
UF_PUSH(frame->locals[instr->operand.var.index]);
UF_DISPATCH();
uf_store_local:
frame->locals[instr->operand.var.index] = UF_POP();
UF_DISPATCH();
uf_add: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
2025-12-03 17:55:37 +01:00
if (LIKELY(rava_nanbox_is_int(a) && rava_nanbox_is_int(b))) {
2025-12-02 06:54:32 +01:00
UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) + rava_nanbox_as_int(b)));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(rava_nanbox_is_string(a) || rava_nanbox_is_string(b))) {
2025-12-02 22:04:26 +01:00
char buf_a[64], buf_b[64];
const char *str_a, *str_b;
if (rava_nanbox_is_string(a)) str_a = rava_nanbox_as_string(a) ? rava_nanbox_as_string(a) : "null";
else if (rava_nanbox_is_int(a)) { snprintf(buf_a, 64, "%d", rava_nanbox_as_int(a)); str_a = buf_a; }
else if (rava_nanbox_is_long(a)) { snprintf(buf_a, 64, "%ld", (long)rava_nanbox_as_long(a)); str_a = buf_a; }
else if (rava_nanbox_is_double(a)) { snprintf(buf_a, 64, "%g", rava_nanbox_as_double(a)); str_a = buf_a; }
else if (rava_nanbox_is_bool(a)) { str_a = rava_nanbox_as_bool(a) ? "true" : "false"; }
else { str_a = "null"; }
if (rava_nanbox_is_string(b)) str_b = rava_nanbox_as_string(b) ? rava_nanbox_as_string(b) : "null";
else if (rava_nanbox_is_int(b)) { snprintf(buf_b, 64, "%d", rava_nanbox_as_int(b)); str_b = buf_b; }
else if (rava_nanbox_is_long(b)) { snprintf(buf_b, 64, "%ld", (long)rava_nanbox_as_long(b)); str_b = buf_b; }
else if (rava_nanbox_is_double(b)) { snprintf(buf_b, 64, "%g", rava_nanbox_as_double(b)); str_b = buf_b; }
else if (rava_nanbox_is_bool(b)) { str_b = rava_nanbox_as_bool(b) ? "true" : "false"; }
else { str_b = "null"; }
2025-12-03 17:55:37 +01:00
size_t len_a = strlen(str_a);
size_t len_b = strlen(str_b);
size_t len = len_a + len_b + 1;
bool need_free = (len >= RAVA_STRING_BUFFER_SIZE);
char *result = need_free ? malloc(len) : _rava_get_string_buffer(vm, len);
memcpy(result, str_a, len_a);
memcpy(result + len_a, str_b, len_b + 1);
2025-12-02 22:04:26 +01:00
UF_PUSH(rava_nanbox_string(result));
2025-12-03 17:55:37 +01:00
if (need_free) free(result);
2025-12-02 06:54:32 +01:00
} else if (rava_nanbox_is_long(a) || rava_nanbox_is_long(b)) {
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) + rava_nanbox_to_long(b)));
} else if (rava_nanbox_is_double(a) || rava_nanbox_is_double(b)) {
UF_PUSH(rava_nanbox_double(rava_nanbox_to_double(a) + rava_nanbox_to_double(b)));
} else {
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) + rava_nanbox_to_long(b)));
}
UF_DISPATCH();
}
uf_sub: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
2025-12-03 17:55:37 +01:00
if (LIKELY(rava_nanbox_is_int(a) && rava_nanbox_is_int(b))) {
2025-12-02 06:54:32 +01:00
UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) - rava_nanbox_as_int(b)));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(rava_nanbox_is_long(a) || rava_nanbox_is_long(b))) {
2025-12-02 06:54:32 +01:00
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) - rava_nanbox_to_long(b)));
} else {
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) - rava_nanbox_to_long(b)));
}
UF_DISPATCH();
}
uf_mul: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
2025-12-03 17:55:37 +01:00
if (LIKELY(rava_nanbox_is_int(a) && rava_nanbox_is_int(b))) {
2025-12-02 06:54:32 +01:00
UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) * rava_nanbox_as_int(b)));
2025-12-03 17:55:37 +01:00
} else if (UNLIKELY(rava_nanbox_is_long(a) || rava_nanbox_is_long(b))) {
2025-12-02 06:54:32 +01:00
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) * rava_nanbox_to_long(b)));
} else {
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) * rava_nanbox_to_long(b)));
}
UF_DISPATCH();
}
uf_div: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
int64_t bv = rava_nanbox_to_long(b);
2025-12-03 17:55:37 +01:00
if (UNLIKELY(bv == 0)) { UF_PUSH(rava_nanbox_int(0)); UF_DISPATCH(); }
if (LIKELY(rava_nanbox_is_int(a) && rava_nanbox_is_int(b))) {
2025-12-02 06:54:32 +01:00
UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) / rava_nanbox_as_int(b)));
} else {
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) / bv));
}
UF_DISPATCH();
}
uf_mod: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
int64_t bv = rava_nanbox_to_long(b);
if (bv == 0) { UF_PUSH(rava_nanbox_int(0)); UF_DISPATCH(); }
if (rava_nanbox_is_int(a) && rava_nanbox_is_int(b)) {
UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) % rava_nanbox_as_int(b)));
} else {
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) % bv));
}
UF_DISPATCH();
}
uf_neg: {
RavaNanboxValue_t a = UF_POP();
if (rava_nanbox_is_int(a)) {
UF_PUSH(rava_nanbox_int(-rava_nanbox_as_int(a)));
} else {
UF_PUSH(rava_nanbox_long(-rava_nanbox_to_long(a)));
}
UF_DISPATCH();
}
uf_and: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
2025-12-02 21:12:50 +01:00
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) & rava_nanbox_to_long(b)));
2025-12-02 06:54:32 +01:00
UF_DISPATCH();
}
uf_or: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
2025-12-02 21:12:50 +01:00
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) | rava_nanbox_to_long(b)));
2025-12-02 06:54:32 +01:00
UF_DISPATCH();
}
uf_xor: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) ^ rava_nanbox_to_long(b)));
UF_DISPATCH();
}
uf_not: {
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_bool(!rava_nanbox_to_bool(a)));
UF_DISPATCH();
}
2025-12-02 22:04:26 +01:00
uf_bitnot: {
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_long(~rava_nanbox_to_long(a)));
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_shl: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) << (rava_nanbox_to_int(b) & 63)));
UF_DISPATCH();
}
uf_shr: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) >> (rava_nanbox_to_int(b) & 63)));
UF_DISPATCH();
}
uf_ushr: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_long((uint64_t)rava_nanbox_to_long(a) >> (rava_nanbox_to_int(b) & 63)));
UF_DISPATCH();
}
uf_eq: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) == rava_nanbox_to_long(b)));
UF_DISPATCH();
}
uf_ne: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) != rava_nanbox_to_long(b)));
UF_DISPATCH();
}
uf_lt: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) < rava_nanbox_to_long(b)));
UF_DISPATCH();
}
uf_le: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) <= rava_nanbox_to_long(b)));
UF_DISPATCH();
}
uf_gt: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) > rava_nanbox_to_long(b)));
UF_DISPATCH();
}
uf_ge: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) >= rava_nanbox_to_long(b)));
UF_DISPATCH();
}
uf_jump: {
LabelTable_t *lt = (LabelTable_t*)frame->method->label_table;
pc = rava_labeltable_lookup(lt, instr->operand.label_id);
UF_DISPATCH();
}
uf_jump_if_true: {
RavaNanboxValue_t cond = UF_POP();
if (rava_nanbox_to_bool(cond)) {
LabelTable_t *lt = (LabelTable_t*)frame->method->label_table;
pc = rava_labeltable_lookup(lt, instr->operand.label_id);
}
UF_DISPATCH();
}
uf_jump_if_false: {
RavaNanboxValue_t cond = UF_POP();
if (!rava_nanbox_to_bool(cond)) {
LabelTable_t *lt = (LabelTable_t*)frame->method->label_table;
pc = rava_labeltable_lookup(lt, instr->operand.label_id);
}
UF_DISPATCH();
}
uf_label:
UF_DISPATCH();
uf_call:
uf_call_static: {
RavaMethod_t *target = _rava_vm_find_method_cached(vm,
instr->operand.call.class_name, instr->operand.call.method_name);
if (!target) {
vm->had_error = true;
goto uf_done;
}
if (!target->label_table) {
rava_optimize_superinstructions(target);
target->label_table = rava_labeltable_create(target->instructions);
}
RavaNanboxValue_t args[16];
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
args[i] = UF_POP();
}
frame->pc = pc;
FastFrame_t *new_frame = rava_fastframe_push(target, target->local_count);
if (!new_frame) {
vm->had_error = true;
goto uf_done;
}
for (int i = 0; i < instr->operand.call.arg_count; i++) {
new_frame->locals[i] = args[i];
}
frame = new_frame;
instructions = target->instructions->instructions;
2025-12-03 23:51:38 +01:00
instr_count = target->instructions->count;
pc = 0;
UF_DISPATCH();
}
uf_call_recursive: {
RavaMethod_t *target = (RavaMethod_t*)instr->operand.call.cached_method;
if (UNLIKELY(!target)) {
target = frame->method;
instr->operand.call.cached_method = target;
}
if (!target->label_table) {
rava_optimize_superinstructions(target);
target->label_table = rava_labeltable_create(target->instructions);
}
RavaNanboxValue_t args[16];
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
args[i] = UF_POP();
}
frame->pc = pc;
FastFrame_t *new_frame = rava_fastframe_push(target, target->local_count);
if (!new_frame) {
vm->had_error = true;
goto uf_done;
}
for (int i = 0; i < instr->operand.call.arg_count; i++) {
new_frame->locals[i] = args[i];
}
frame = new_frame;
instructions = target->instructions->instructions;
2025-12-02 06:54:32 +01:00
instr_count = target->instructions->count;
pc = 0;
UF_DISPATCH();
}
2025-12-04 06:14:22 +01:00
uf_call_inline: {
RavaMethod_t *target = (RavaMethod_t*)instr->operand.call.cached_method;
if (UNLIKELY(!target)) {
vm->had_error = true;
goto uf_done;
}
if (!target->label_table) {
rava_optimize_superinstructions(target);
target->label_table = rava_labeltable_create(target->instructions);
}
RavaNanboxValue_t args[16];
for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) {
args[i] = UF_POP();
}
frame->pc = pc;
FastFrame_t *new_frame = rava_fastframe_push(target, target->local_count);
if (!new_frame) {
vm->had_error = true;
goto uf_done;
}
for (int i = 0; i < instr->operand.call.arg_count; i++) {
new_frame->locals[i] = args[i];
}
frame = new_frame;
instructions = target->instructions->instructions;
instr_count = target->instructions->count;
pc = 0;
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_call_virtual: {
int arg_count = instr->operand.call.arg_count;
RavaNanboxValue_t args[16];
for (int i = arg_count - 1; i >= 0; i--) {
args[i] = UF_POP();
}
RavaNanboxValue_t obj_val = UF_POP();
if (rava_nanbox_is_null(obj_val)) {
vm->had_error = true;
goto uf_done;
}
2025-12-04 23:51:05 +01:00
const char *mname = instr->operand.call.method_name;
if (!rava_nanbox_is_object(obj_val)) {
RavaValue_t val = rava_nanbox_to_value(obj_val);
if (strcmp(mname, "hashCode") == 0 && arg_count == 0) {
UF_PUSH(rava_nanbox_int(rava_object_hashcode(val)));
UF_DISPATCH();
}
if (strcmp(mname, "equals") == 0 && arg_count == 1) {
RavaValue_t other = rava_nanbox_to_value(args[0]);
UF_PUSH(rava_nanbox_bool(rava_object_equals(val, other)));
UF_DISPATCH();
}
if (strcmp(mname, "toString") == 0 && arg_count == 0) {
char *str = rava_object_tostring(val);
UF_PUSH(rava_nanbox_string(str));
UF_DISPATCH();
}
if (strcmp(mname, "getClass") == 0 && arg_count == 0) {
UF_PUSH(rava_nanbox_string(rava_object_getclass(val)));
UF_DISPATCH();
}
vm->had_error = true;
goto uf_done;
}
2025-12-02 06:54:32 +01:00
RavaObject_t *obj = rava_nanbox_as_object(obj_val);
const char *class_name = obj->class_name;
RavaMethod_t *target = _rava_vm_find_method_cached(vm, class_name, instr->operand.call.method_name);
if (!target) {
2025-12-04 23:51:05 +01:00
if (strcmp(mname, "<init>") == 0 && arg_count == 0) {
UF_DISPATCH();
}
RavaValue_t val = rava_nanbox_to_value(obj_val);
if (strcmp(mname, "hashCode") == 0 && arg_count == 0) {
UF_PUSH(rava_nanbox_int(rava_object_hashcode(val)));
UF_DISPATCH();
}
if (strcmp(mname, "equals") == 0 && arg_count == 1) {
RavaValue_t other = rava_nanbox_to_value(args[0]);
UF_PUSH(rava_nanbox_bool(rava_object_equals(val, other)));
UF_DISPATCH();
}
if (strcmp(mname, "toString") == 0 && arg_count == 0) {
char *str = rava_object_tostring(val);
UF_PUSH(rava_nanbox_string(str));
UF_DISPATCH();
}
if (strcmp(mname, "getClass") == 0 && arg_count == 0) {
UF_PUSH(rava_nanbox_string(rava_object_getclass(val)));
2025-12-02 06:54:32 +01:00
UF_DISPATCH();
}
vm->had_error = true;
goto uf_done;
}
if (!target->label_table) {
rava_optimize_superinstructions(target);
target->label_table = rava_labeltable_create(target->instructions);
}
frame->pc = pc;
FastFrame_t *new_frame = rava_fastframe_push(target, target->local_count);
if (!new_frame) {
vm->had_error = true;
goto uf_done;
}
new_frame->has_this = true;
new_frame->this_ref = obj_val;
for (int i = 0; i < arg_count; i++) {
new_frame->locals[i] = args[i];
}
frame = new_frame;
instructions = target->instructions->instructions;
instr_count = target->instructions->count;
pc = 0;
UF_DISPATCH();
}
2025-12-02 21:12:50 +01:00
uf_call_super: {
int arg_count = instr->operand.call.arg_count;
RavaNanboxValue_t args[16];
for (int i = arg_count - 1; i >= 0; i--) {
args[i] = UF_POP();
}
RavaNanboxValue_t obj_val = UF_POP();
const char *super_class = instr->operand.call.class_name;
RavaMethod_t *target = _rava_vm_find_method_cached(vm, super_class, instr->operand.call.method_name);
if (!target) {
vm->had_error = true;
goto uf_done;
}
if (!target->label_table) {
rava_optimize_superinstructions(target);
target->label_table = rava_labeltable_create(target->instructions);
}
frame->pc = pc;
FastFrame_t *new_frame = rava_fastframe_push(target, target->local_count);
if (!new_frame) {
vm->had_error = true;
goto uf_done;
}
new_frame->has_this = true;
new_frame->this_ref = obj_val;
for (int i = 0; i < arg_count; i++) {
new_frame->locals[i] = args[i];
}
frame = new_frame;
instructions = target->instructions->instructions;
instr_count = target->instructions->count;
pc = 0;
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_call_native:
UF_DISPATCH();
uf_return: {
result = UF_POP();
rava_fastframe_pop();
frame = rava_fastframe_current();
if (!frame) {
RavaCallFrame_t *old_frame = rava_call_stack_current(vm->call_stack);
if (old_frame) {
rava_stack_push(old_frame->operand_stack, rava_nanbox_to_value(result));
}
goto uf_done;
}
UF_PUSH(result);
pc = frame->pc;
instructions = frame->method->instructions->instructions;
instr_count = frame->method->instructions->count;
UF_DISPATCH();
}
uf_return_void: {
rava_fastframe_pop();
frame = rava_fastframe_current();
if (!frame) goto uf_done;
pc = frame->pc;
instructions = frame->method->instructions->instructions;
instr_count = frame->method->instructions->count;
UF_DISPATCH();
}
uf_new: {
RavaObject_t *obj = rava_object_create(instr->operand.call.class_name);
UF_PUSH(rava_nanbox_object(obj));
UF_DISPATCH();
}
uf_new_array: {
RavaNanboxValue_t size_val = UF_POP();
int32_t size = rava_nanbox_to_int(size_val);
RavaArray_t *arr = rava_array_create(RAVA_VAL_INT, size > 0 ? (size_t)size : 0);
UF_PUSH(rava_nanbox_array(arr));
UF_DISPATCH();
}
2025-12-03 14:08:55 +01:00
uf_new_array_of_arrays: {
RavaNanboxValue_t size_val = UF_POP();
int32_t size = rava_nanbox_to_int(size_val);
RavaArray_t *arr = rava_array_create(RAVA_VAL_ARRAY, size > 0 ? (size_t)size : 0);
UF_PUSH(rava_nanbox_array(arr));
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_array_length: {
RavaNanboxValue_t arr_val = UF_POP();
RavaArray_t *arr = rava_nanbox_as_array(arr_val);
UF_PUSH(rava_nanbox_int(arr ? (int32_t)arr->length : 0));
UF_DISPATCH();
}
uf_load_array: {
RavaNanboxValue_t idx = UF_POP();
RavaNanboxValue_t arr_val = UF_POP();
RavaArray_t *arr = rava_nanbox_as_array(arr_val);
int32_t i = rava_nanbox_to_int(idx);
if (arr && i >= 0 && (size_t)i < arr->length) {
2025-12-03 14:08:55 +01:00
if (arr->element_type == RAVA_VAL_ARRAY || arr->element_type == RAVA_VAL_OBJECT) {
RavaValue_t v = rava_array_get_value(arr, (size_t)i);
UF_PUSH(rava_value_to_nanbox(v));
} else {
int32_t val = rava_array_get_int(arr, (size_t)i);
UF_PUSH(rava_nanbox_int(val));
}
2025-12-02 06:54:32 +01:00
} else {
UF_PUSH(rava_nanbox_int(0));
}
UF_DISPATCH();
}
2025-12-04 06:14:22 +01:00
uf_load_array_unchecked: {
RavaNanboxValue_t idx = UF_POP();
RavaNanboxValue_t arr_val = UF_POP();
RavaArray_t *arr = rava_nanbox_as_array(arr_val);
size_t i = (size_t)rava_nanbox_to_int(idx);
if (arr->element_type == RAVA_VAL_ARRAY || arr->element_type == RAVA_VAL_OBJECT) {
RavaValue_t v = rava_array_get_value(arr, i);
UF_PUSH(rava_value_to_nanbox(v));
} else {
int32_t val = rava_array_get_int(arr, i);
UF_PUSH(rava_nanbox_int(val));
}
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_store_array: {
RavaNanboxValue_t val = UF_POP();
RavaNanboxValue_t idx = UF_POP();
RavaNanboxValue_t arr_val = UF_POP();
RavaArray_t *arr = rava_nanbox_as_array(arr_val);
int32_t i = rava_nanbox_to_int(idx);
if (arr && i >= 0 && (size_t)i < arr->length) {
2025-12-03 14:08:55 +01:00
if (arr->element_type == RAVA_VAL_ARRAY || arr->element_type == RAVA_VAL_OBJECT) {
rava_array_set_value(arr, (size_t)i, rava_nanbox_to_value(val));
} else {
rava_array_set_int(arr, (size_t)i, rava_nanbox_to_int(val));
}
2025-12-02 06:54:32 +01:00
}
UF_DISPATCH();
}
2025-12-04 06:14:22 +01:00
uf_store_array_unchecked: {
RavaNanboxValue_t val = UF_POP();
RavaNanboxValue_t idx = UF_POP();
RavaNanboxValue_t arr_val = UF_POP();
RavaArray_t *arr = rava_nanbox_as_array(arr_val);
size_t i = (size_t)rava_nanbox_to_int(idx);
if (arr->element_type == RAVA_VAL_ARRAY || arr->element_type == RAVA_VAL_OBJECT) {
rava_array_set_value(arr, i, rava_nanbox_to_value(val));
} else {
rava_array_set_int(arr, i, rava_nanbox_to_int(val));
}
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_get_field:
uf_load_field: {
RavaNanboxValue_t obj_val = UF_POP();
RavaObject_t *obj = rava_nanbox_as_object(obj_val);
if (obj) {
RavaValue_t v = rava_object_get_field(obj, instr->operand.field.field_name);
UF_PUSH(rava_value_to_nanbox(v));
} else {
UF_PUSH(rava_nanbox_null());
}
UF_DISPATCH();
}
uf_put_field:
uf_store_field: {
RavaNanboxValue_t val = UF_POP();
RavaNanboxValue_t obj_val = UF_POP();
RavaObject_t *obj = rava_nanbox_as_object(obj_val);
if (obj) {
2025-12-03 17:55:37 +01:00
_rava_object_set_field_vm(obj, instr->operand.field.field_name, rava_nanbox_to_value(val), vm);
2025-12-02 06:54:32 +01:00
}
UF_DISPATCH();
}
uf_load_static: {
RavaValue_t *v = rava_static_field_table_get(vm->static_fields, instr->operand.field.class_name, instr->operand.field.field_name);
UF_PUSH(v ? rava_value_to_nanbox(*v) : rava_nanbox_null());
UF_DISPATCH();
}
uf_store_static: {
RavaNanboxValue_t val = UF_POP();
rava_static_field_table_set(vm->static_fields, instr->operand.field.class_name, instr->operand.field.field_name, rava_nanbox_to_value(val));
UF_DISPATCH();
}
2025-12-02 21:12:50 +01:00
uf_string_indexof: {
RavaNanboxValue_t needle = UF_POP();
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
const char *nstr = rava_nanbox_as_string(needle);
if (str && nstr) {
const char *found = strstr(str, nstr);
UF_PUSH(rava_nanbox_int(found ? (int32_t)(found - str) : -1));
} else {
UF_PUSH(rava_nanbox_int(-1));
}
UF_DISPATCH();
}
uf_string_contains: {
RavaNanboxValue_t needle = UF_POP();
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
const char *nstr = rava_nanbox_as_string(needle);
UF_PUSH(rava_nanbox_bool(str && nstr && strstr(str, nstr) != NULL));
UF_DISPATCH();
}
uf_string_startswith: {
RavaNanboxValue_t prefix = UF_POP();
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
const char *pstr = rava_nanbox_as_string(prefix);
if (str && pstr) {
size_t plen = strlen(pstr);
UF_PUSH(rava_nanbox_bool(strncmp(str, pstr, plen) == 0));
} else {
UF_PUSH(rava_nanbox_bool(false));
}
UF_DISPATCH();
}
uf_string_endswith: {
RavaNanboxValue_t suffix = UF_POP();
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
const char *sstr = rava_nanbox_as_string(suffix);
if (str && sstr) {
size_t slen = strlen(str);
size_t suflen = strlen(sstr);
if (suflen <= slen) {
UF_PUSH(rava_nanbox_bool(strcmp(str + slen - suflen, sstr) == 0));
} else {
UF_PUSH(rava_nanbox_bool(false));
}
} else {
UF_PUSH(rava_nanbox_bool(false));
}
UF_DISPATCH();
}
uf_string_tolowercase: {
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
if (str) {
size_t len = strlen(str);
char *lower = malloc(len + 1);
for (size_t i = 0; i <= len; i++) {
lower[i] = (char)tolower((unsigned char)str[i]);
}
UF_PUSH(rava_nanbox_string(lower));
} else {
UF_PUSH(rava_nanbox_string(""));
}
UF_DISPATCH();
}
uf_string_touppercase: {
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
if (str) {
size_t len = strlen(str);
char *upper = malloc(len + 1);
for (size_t i = 0; i <= len; i++) {
upper[i] = (char)toupper((unsigned char)str[i]);
}
UF_PUSH(rava_nanbox_string(upper));
} else {
UF_PUSH(rava_nanbox_string(""));
}
UF_DISPATCH();
}
uf_string_trim: {
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
if (str) {
const char *start = str;
while (*start && isspace((unsigned char)*start)) start++;
const char *end = str + strlen(str);
while (end > start && isspace((unsigned char)*(end - 1))) end--;
size_t len = (size_t)(end - start);
char *trimmed = malloc(len + 1);
strncpy(trimmed, start, len);
trimmed[len] = '\0';
UF_PUSH(rava_nanbox_string(trimmed));
} else {
UF_PUSH(rava_nanbox_string(""));
}
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_load_this:
if (frame->has_this) {
UF_PUSH(frame->this_ref);
} else {
UF_PUSH(rava_nanbox_null());
}
UF_DISPATCH();
uf_pop:
UF_POP();
UF_DISPATCH();
uf_dup: {
RavaNanboxValue_t v = UF_PEEK();
UF_PUSH(v);
UF_DISPATCH();
}
uf_print: {
RavaNanboxValue_t v = UF_POP();
if (rava_nanbox_is_int(v)) {
printf("%d", rava_nanbox_as_int(v));
} else if (rava_nanbox_is_long(v)) {
printf("%ld", rava_nanbox_as_long(v));
} else if (rava_nanbox_is_double(v)) {
printf("%g", rava_nanbox_as_double(v));
} else if (rava_nanbox_is_bool(v)) {
printf("%s", rava_nanbox_as_bool(v) ? "true" : "false");
} else if (rava_nanbox_is_string(v)) {
printf("%s", rava_nanbox_as_string(v));
} else if (rava_nanbox_is_null(v)) {
printf("null");
}
UF_DISPATCH();
}
uf_println: {
RavaNanboxValue_t v = UF_POP();
if (rava_nanbox_is_int(v)) {
printf("%d\n", rava_nanbox_as_int(v));
} else if (rava_nanbox_is_long(v)) {
printf("%ld\n", rava_nanbox_as_long(v));
} else if (rava_nanbox_is_double(v)) {
printf("%g\n", rava_nanbox_as_double(v));
} else if (rava_nanbox_is_bool(v)) {
printf("%s\n", rava_nanbox_as_bool(v) ? "true" : "false");
} else if (rava_nanbox_is_string(v)) {
printf("%s\n", rava_nanbox_as_string(v));
} else if (rava_nanbox_is_null(v)) {
printf("null\n");
}
UF_DISPATCH();
}
uf_string_length: {
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
UF_PUSH(rava_nanbox_int(str ? (int32_t)strlen(str) : 0));
UF_DISPATCH();
}
uf_string_charat: {
RavaNanboxValue_t idx = UF_POP();
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
int32_t i = rava_nanbox_to_int(idx);
if (str && i >= 0 && (size_t)i < strlen(str)) {
UF_PUSH(rava_nanbox_int((int32_t)str[i]));
} else {
UF_PUSH(rava_nanbox_int(0));
}
UF_DISPATCH();
}
uf_string_substring: {
RavaNanboxValue_t end_val = UF_POP();
RavaNanboxValue_t start_val = UF_POP();
RavaNanboxValue_t s = UF_POP();
const char *str = rava_nanbox_as_string(s);
int32_t start = rava_nanbox_to_int(start_val);
int32_t end = rava_nanbox_to_int(end_val);
if (str && start >= 0 && end >= start && (size_t)end <= strlen(str)) {
size_t len = (size_t)(end - start);
char *sub = malloc(len + 1);
strncpy(sub, str + start, len);
sub[len] = '\0';
UF_PUSH(rava_nanbox_string(sub));
} else {
UF_PUSH(rava_nanbox_string(""));
}
UF_DISPATCH();
}
uf_string_equals: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
const char *sa = rava_nanbox_as_string(a);
const char *sb = rava_nanbox_as_string(b);
UF_PUSH(rava_nanbox_bool(sa && sb && strcmp(sa, sb) == 0));
UF_DISPATCH();
}
uf_string_compareto: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
const char *sa = rava_nanbox_as_string(a);
const char *sb = rava_nanbox_as_string(b);
if (sa && sb) {
UF_PUSH(rava_nanbox_int(strcmp(sa, sb)));
} else {
UF_PUSH(rava_nanbox_int(0));
}
UF_DISPATCH();
}
2025-12-02 21:12:50 +01:00
uf_cast: {
RavaNanboxValue_t val = UF_POP();
RavaType_t *target_type = instr->operand.var.type;
if (target_type) {
switch (target_type->kind) {
case RAVA_TYPE_INT:
if (rava_nanbox_is_double(val)) {
UF_PUSH(rava_nanbox_int((int32_t)rava_nanbox_to_double(val)));
} else if (rava_nanbox_is_long(val)) {
UF_PUSH(rava_nanbox_int((int32_t)rava_nanbox_to_long(val)));
} else {
UF_PUSH(val);
}
break;
case RAVA_TYPE_LONG:
if (rava_nanbox_is_double(val)) {
UF_PUSH(rava_nanbox_long((int64_t)rava_nanbox_to_double(val)));
} else if (rava_nanbox_is_int(val)) {
UF_PUSH(rava_nanbox_long((int64_t)rava_nanbox_to_int(val)));
} else {
UF_PUSH(val);
}
break;
case RAVA_TYPE_DOUBLE:
case RAVA_TYPE_FLOAT:
if (rava_nanbox_is_int(val)) {
UF_PUSH(rava_nanbox_double((double)rava_nanbox_to_int(val)));
} else if (rava_nanbox_is_long(val)) {
UF_PUSH(rava_nanbox_double((double)rava_nanbox_to_long(val)));
} else {
UF_PUSH(val);
}
break;
default:
UF_PUSH(val);
break;
}
} else {
UF_PUSH(val);
}
2025-12-02 06:54:32 +01:00
UF_DISPATCH();
2025-12-02 21:12:50 +01:00
}
uf_instanceof: {
RavaNanboxValue_t val = UF_POP();
bool result = false;
if (rava_nanbox_is_object(val)) {
RavaObject_t *obj = rava_nanbox_as_object(val);
if (obj) {
result = strcmp(obj->class_name, instr->operand.string_value) == 0;
}
}
UF_PUSH(rava_nanbox_bool(result));
UF_DISPATCH();
}
uf_throw: {
RavaNanboxValue_t exception = UF_POP();
vm->has_exception = true;
vm->exception_value = rava_nanbox_to_value(exception);
RavaExceptionHandler_t handler;
if (rava_exception_stack_pop(vm->exception_stack, &handler)) {
frame->locals[handler.exception_local] = exception;
LabelTable_t *lt = (LabelTable_t*)frame->method->label_table;
pc = rava_labeltable_lookup(lt, handler.catch_label);
vm->has_exception = false;
UF_DISPATCH();
} else {
vm->had_error = true;
goto uf_done;
}
}
uf_try_begin: {
RavaExceptionHandler_t handler;
handler.catch_label = instr->operand.try_handler.catch_label;
handler.finally_label = instr->operand.try_handler.finally_label;
handler.end_label = instr->operand.try_handler.end_label;
handler.exception_local = instr->operand.try_handler.exception_local;
handler.stack_depth = frame->stack_top;
rava_exception_stack_push(vm->exception_stack, handler);
UF_DISPATCH();
}
uf_try_end: {
RavaExceptionHandler_t handler;
rava_exception_stack_pop(vm->exception_stack, &handler);
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_file_read:
uf_file_write:
uf_file_exists:
uf_file_delete:
UF_DISPATCH();
uf_current_time_millis: {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
int64_t ms = (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec / 1000000;
UF_PUSH(rava_nanbox_long(ms));
UF_DISPATCH();
}
uf_nano_time: {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int64_t ns = (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec;
UF_PUSH(rava_nanbox_long(ns));
UF_DISPATCH();
}
uf_inc_local: {
int idx = instr->operand.var.index;
RavaNanboxValue_t v = frame->locals[idx];
if (rava_nanbox_is_int(v)) {
frame->locals[idx] = rava_nanbox_int(rava_nanbox_as_int(v) + 1);
} else {
frame->locals[idx] = rava_nanbox_long(rava_nanbox_to_long(v) + 1);
}
UF_DISPATCH();
}
uf_dec_local: {
int idx = instr->operand.var.index;
RavaNanboxValue_t v = frame->locals[idx];
if (rava_nanbox_is_int(v)) {
frame->locals[idx] = rava_nanbox_int(rava_nanbox_as_int(v) - 1);
} else {
frame->locals[idx] = rava_nanbox_long(rava_nanbox_to_long(v) - 1);
}
UF_DISPATCH();
}
uf_load_local_const_add: {
int idx = instr->operand.super.local_index;
int64_t c = instr->operand.super.const_value;
RavaNanboxValue_t v = frame->locals[idx];
if (rava_nanbox_is_int(v)) {
UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(v) + (int32_t)c));
} else {
UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(v) + c));
}
UF_DISPATCH();
}
uf_load_local_const_lt_jumpfalse: {
int idx = instr->operand.super.local_index;
int64_t c = instr->operand.super.const_value;
int label = instr->operand.super.label_id;
RavaNanboxValue_t v = frame->locals[idx];
int64_t local_val = rava_nanbox_to_long(v);
if (!(local_val < c)) {
LabelTable_t *lt = (LabelTable_t*)frame->method->label_table;
pc = rava_labeltable_lookup(lt, label);
}
UF_DISPATCH();
}
uf_load_local_const_le_jumpfalse: {
int idx = instr->operand.super.local_index;
int64_t c = instr->operand.super.const_value;
int label = instr->operand.super.label_id;
RavaNanboxValue_t v = frame->locals[idx];
int64_t local_val = rava_nanbox_to_long(v);
if (!(local_val <= c)) {
LabelTable_t *lt = (LabelTable_t*)frame->method->label_table;
pc = rava_labeltable_lookup(lt, label);
}
UF_DISPATCH();
}
uf_load_two_locals: {
int idx1 = instr->operand.two_locals.index1;
int idx2 = instr->operand.two_locals.index2;
UF_PUSH(frame->locals[idx1]);
UF_PUSH(frame->locals[idx2]);
UF_DISPATCH();
}
uf_add_local_to_local: {
int dest = instr->operand.add_locals.dest_index;
int src = instr->operand.add_locals.src_index;
RavaNanboxValue_t d = frame->locals[dest];
RavaNanboxValue_t s = frame->locals[src];
if (rava_nanbox_is_int(d) && rava_nanbox_is_int(s)) {
frame->locals[dest] = rava_nanbox_int(rava_nanbox_as_int(d) + rava_nanbox_as_int(s));
} else {
frame->locals[dest] = rava_nanbox_long(rava_nanbox_to_long(d) + rava_nanbox_to_long(s));
}
UF_DISPATCH();
}
uf_load_local_lt_local_jumpfalse: {
int local1 = instr->operand.cmp_locals.local1;
int local2 = instr->operand.cmp_locals.local2;
int label = instr->operand.cmp_locals.label_id;
int64_t v1 = rava_nanbox_to_long(frame->locals[local1]);
int64_t v2 = rava_nanbox_to_long(frame->locals[local2]);
if (!(v1 < v2)) {
LabelTable_t *lt = (LabelTable_t*)frame->method->label_table;
pc = rava_labeltable_lookup(lt, label);
}
UF_DISPATCH();
}
2025-12-02 21:12:50 +01:00
uf_math_abs: {
RavaNanboxValue_t val = UF_POP();
if (rava_nanbox_is_double(val)) {
double d = rava_nanbox_to_double(val);
UF_PUSH(rava_nanbox_double(fabs(d)));
} else {
int64_t v = rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_long(v < 0 ? -v : v));
}
UF_DISPATCH();
}
uf_math_sqrt: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(sqrt(d)));
UF_DISPATCH();
}
uf_math_pow: {
RavaNanboxValue_t exp = UF_POP();
RavaNanboxValue_t base = UF_POP();
double b = rava_nanbox_is_double(base) ? rava_nanbox_to_double(base) : (double)rava_nanbox_to_long(base);
double e = rava_nanbox_is_double(exp) ? rava_nanbox_to_double(exp) : (double)rava_nanbox_to_long(exp);
UF_PUSH(rava_nanbox_double(pow(b, e)));
UF_DISPATCH();
}
uf_math_min: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
if (rava_nanbox_is_double(a) || rava_nanbox_is_double(b)) {
double da = rava_nanbox_is_double(a) ? rava_nanbox_to_double(a) : (double)rava_nanbox_to_long(a);
double db = rava_nanbox_is_double(b) ? rava_nanbox_to_double(b) : (double)rava_nanbox_to_long(b);
UF_PUSH(rava_nanbox_double(da < db ? da : db));
} else {
int64_t ia = rava_nanbox_to_long(a);
int64_t ib = rava_nanbox_to_long(b);
UF_PUSH(rava_nanbox_long(ia < ib ? ia : ib));
}
UF_DISPATCH();
}
uf_math_max: {
RavaNanboxValue_t b = UF_POP();
RavaNanboxValue_t a = UF_POP();
if (rava_nanbox_is_double(a) || rava_nanbox_is_double(b)) {
double da = rava_nanbox_is_double(a) ? rava_nanbox_to_double(a) : (double)rava_nanbox_to_long(a);
double db = rava_nanbox_is_double(b) ? rava_nanbox_to_double(b) : (double)rava_nanbox_to_long(b);
UF_PUSH(rava_nanbox_double(da > db ? da : db));
} else {
int64_t ia = rava_nanbox_to_long(a);
int64_t ib = rava_nanbox_to_long(b);
UF_PUSH(rava_nanbox_long(ia > ib ? ia : ib));
}
UF_DISPATCH();
}
uf_math_floor: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(floor(d)));
UF_DISPATCH();
}
uf_math_ceil: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(ceil(d)));
UF_DISPATCH();
}
uf_math_round: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_long((int64_t)round(d)));
UF_DISPATCH();
}
uf_math_sin: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(sin(d)));
UF_DISPATCH();
}
uf_math_cos: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(cos(d)));
UF_DISPATCH();
}
uf_math_tan: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(tan(d)));
UF_DISPATCH();
}
uf_math_log: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(log(d)));
UF_DISPATCH();
}
uf_math_exp: {
RavaNanboxValue_t val = UF_POP();
double d = rava_nanbox_is_double(val) ? rava_nanbox_to_double(val) : (double)rava_nanbox_to_long(val);
UF_PUSH(rava_nanbox_double(exp(d)));
UF_DISPATCH();
}
uf_math_random: {
UF_PUSH(rava_nanbox_double((double)rand() / (double)RAND_MAX));
UF_DISPATCH();
}
2025-12-04 16:49:20 +01:00
uf_arraylist_new: {
RavaArrayList_t *list = rava_arraylist_create();
UF_PUSH(rava_nanbox_object(list));
UF_DISPATCH();
}
uf_arraylist_add: {
RavaNanboxValue_t val = UF_POP();
RavaNanboxValue_t list_nb = UF_POP();
RavaArrayList_t *list = (RavaArrayList_t*)rava_nanbox_as_object(list_nb);
if (list) {
RavaValue_t v;
if (rava_nanbox_is_int(val)) {
v = rava_value_int(rava_nanbox_to_int(val));
} else if (rava_nanbox_is_double(val)) {
v = rava_value_double(rava_nanbox_to_double(val));
} else {
v = rava_value_null();
}
rava_arraylist_add(list, v);
}
UF_DISPATCH();
}
uf_arraylist_get: {
RavaNanboxValue_t idx_nb = UF_POP();
RavaNanboxValue_t coll_nb = UF_POP();
void *ptr = rava_nanbox_as_object(coll_nb);
if (rava_nanbox_is_int(idx_nb)) {
RavaArrayList_t *list = (RavaArrayList_t*)ptr;
if (list) {
RavaValue_t v = rava_arraylist_get(list, (size_t)rava_nanbox_to_int(idx_nb));
if (v.type == RAVA_VAL_INT) {
UF_PUSH(rava_nanbox_int(v.data.int_val));
} else if (v.type == RAVA_VAL_DOUBLE) {
UF_PUSH(rava_nanbox_double(v.data.double_val));
} else {
UF_PUSH(rava_nanbox_null());
}
} else {
UF_PUSH(rava_nanbox_null());
}
} else if (rava_nanbox_is_string(idx_nb)) {
RavaHashMap_t *map = (RavaHashMap_t*)ptr;
if (map) {
RavaValue_t v = rava_hashmap_get(map, rava_nanbox_as_string(idx_nb));
if (v.type == RAVA_VAL_INT) {
UF_PUSH(rava_nanbox_int(v.data.int_val));
} else if (v.type == RAVA_VAL_DOUBLE) {
UF_PUSH(rava_nanbox_double(v.data.double_val));
} else {
UF_PUSH(rava_nanbox_null());
}
} else {
UF_PUSH(rava_nanbox_null());
}
} else {
UF_PUSH(rava_nanbox_null());
}
UF_DISPATCH();
}
uf_arraylist_set: {
RavaNanboxValue_t val = UF_POP();
RavaNanboxValue_t idx_nb = UF_POP();
RavaNanboxValue_t list_nb = UF_POP();
RavaArrayList_t *list = (RavaArrayList_t*)rava_nanbox_as_object(list_nb);
if (list) {
RavaValue_t v;
if (rava_nanbox_is_int(val)) {
v = rava_value_int(rava_nanbox_to_int(val));
} else if (rava_nanbox_is_double(val)) {
v = rava_value_double(rava_nanbox_to_double(val));
} else {
v = rava_value_null();
}
rava_arraylist_set(list, (size_t)rava_nanbox_to_int(idx_nb), v);
}
UF_DISPATCH();
}
uf_arraylist_size: {
RavaNanboxValue_t coll_nb = UF_POP();
void *ptr = rava_nanbox_as_object(coll_nb);
if (ptr) {
int type = *(int*)ptr;
if (type == RAVA_COLLECTION_ARRAYLIST) {
UF_PUSH(rava_nanbox_int((int32_t)rava_arraylist_size((RavaArrayList_t*)ptr)));
} else if (type == RAVA_COLLECTION_HASHMAP) {
UF_PUSH(rava_nanbox_int((int32_t)rava_hashmap_size((RavaHashMap_t*)ptr)));
} else {
UF_PUSH(rava_nanbox_int(0));
}
} else {
UF_PUSH(rava_nanbox_int(0));
}
UF_DISPATCH();
}
uf_arraylist_remove: {
RavaNanboxValue_t idx_or_key = UF_POP();
RavaNanboxValue_t coll_nb = UF_POP();
void *ptr = rava_nanbox_as_object(coll_nb);
if (ptr) {
int type = *(int*)ptr;
if (type == RAVA_COLLECTION_ARRAYLIST && rava_nanbox_is_int(idx_or_key)) {
RavaValue_t v = rava_arraylist_remove((RavaArrayList_t*)ptr, (size_t)rava_nanbox_to_int(idx_or_key));
if (v.type == RAVA_VAL_INT) {
UF_PUSH(rava_nanbox_int(v.data.int_val));
} else {
UF_PUSH(rava_nanbox_null());
}
} else if (type == RAVA_COLLECTION_HASHMAP && rava_nanbox_is_string(idx_or_key)) {
RavaValue_t v = rava_hashmap_remove((RavaHashMap_t*)ptr, rava_nanbox_as_string(idx_or_key));
if (v.type == RAVA_VAL_INT) {
UF_PUSH(rava_nanbox_int(v.data.int_val));
} else {
UF_PUSH(rava_nanbox_null());
}
} else {
UF_PUSH(rava_nanbox_null());
}
} else {
UF_PUSH(rava_nanbox_null());
}
UF_DISPATCH();
}
uf_arraylist_clear: {
RavaNanboxValue_t coll_nb = UF_POP();
void *ptr = rava_nanbox_as_object(coll_nb);
if (ptr) {
int type = *(int*)ptr;
if (type == RAVA_COLLECTION_ARRAYLIST) {
rava_arraylist_clear((RavaArrayList_t*)ptr);
} else if (type == RAVA_COLLECTION_HASHMAP) {
rava_hashmap_clear((RavaHashMap_t*)ptr);
}
}
UF_DISPATCH();
}
uf_arraylist_isempty: {
RavaNanboxValue_t list_nb = UF_POP();
RavaArrayList_t *list = (RavaArrayList_t*)rava_nanbox_as_object(list_nb);
if (list) {
UF_PUSH(rava_nanbox_int(rava_arraylist_isempty(list) ? 1 : 0));
} else {
UF_PUSH(rava_nanbox_int(1));
}
UF_DISPATCH();
}
uf_hashmap_new: {
RavaHashMap_t *map = rava_hashmap_create();
UF_PUSH(rava_nanbox_object(map));
UF_DISPATCH();
}
uf_hashmap_put: {
RavaNanboxValue_t val = UF_POP();
RavaNanboxValue_t key_nb = UF_POP();
RavaNanboxValue_t map_nb = UF_POP();
RavaHashMap_t *map = (RavaHashMap_t*)rava_nanbox_as_object(map_nb);
if (map && rava_nanbox_is_string(key_nb)) {
RavaValue_t v;
if (rava_nanbox_is_int(val)) {
v = rava_value_int(rava_nanbox_to_int(val));
} else if (rava_nanbox_is_double(val)) {
v = rava_value_double(rava_nanbox_to_double(val));
} else {
v = rava_value_null();
}
rava_hashmap_put(map, rava_nanbox_as_string(key_nb), v);
}
UF_DISPATCH();
}
uf_hashmap_get: {
RavaNanboxValue_t key_nb = UF_POP();
RavaNanboxValue_t map_nb = UF_POP();
RavaHashMap_t *map = (RavaHashMap_t*)rava_nanbox_as_object(map_nb);
if (map && rava_nanbox_is_string(key_nb)) {
RavaValue_t v = rava_hashmap_get(map, rava_nanbox_as_string(key_nb));
if (v.type == RAVA_VAL_INT) {
UF_PUSH(rava_nanbox_int(v.data.int_val));
} else if (v.type == RAVA_VAL_DOUBLE) {
UF_PUSH(rava_nanbox_double(v.data.double_val));
} else {
UF_PUSH(rava_nanbox_null());
}
} else {
UF_PUSH(rava_nanbox_null());
}
UF_DISPATCH();
}
uf_hashmap_remove: {
RavaNanboxValue_t key_nb = UF_POP();
RavaNanboxValue_t map_nb = UF_POP();
RavaHashMap_t *map = (RavaHashMap_t*)rava_nanbox_as_object(map_nb);
if (map && rava_nanbox_is_string(key_nb)) {
RavaValue_t v = rava_hashmap_remove(map, rava_nanbox_as_string(key_nb));
if (v.type == RAVA_VAL_INT) {
UF_PUSH(rava_nanbox_int(v.data.int_val));
} else {
UF_PUSH(rava_nanbox_null());
}
} else {
UF_PUSH(rava_nanbox_null());
}
UF_DISPATCH();
}
uf_hashmap_size: {
RavaNanboxValue_t map_nb = UF_POP();
RavaHashMap_t *map = (RavaHashMap_t*)rava_nanbox_as_object(map_nb);
if (map) {
UF_PUSH(rava_nanbox_int((int32_t)rava_hashmap_size(map)));
} else {
UF_PUSH(rava_nanbox_int(0));
}
UF_DISPATCH();
}
uf_hashmap_containskey: {
RavaNanboxValue_t key_nb = UF_POP();
RavaNanboxValue_t map_nb = UF_POP();
RavaHashMap_t *map = (RavaHashMap_t*)rava_nanbox_as_object(map_nb);
if (map && rava_nanbox_is_string(key_nb)) {
UF_PUSH(rava_nanbox_int(rava_hashmap_containskey(map, rava_nanbox_as_string(key_nb)) ? 1 : 0));
} else {
UF_PUSH(rava_nanbox_int(0));
}
UF_DISPATCH();
}
uf_hashmap_clear: {
RavaNanboxValue_t map_nb = UF_POP();
RavaHashMap_t *map = (RavaHashMap_t*)rava_nanbox_as_object(map_nb);
if (map) {
rava_hashmap_clear(map);
}
UF_DISPATCH();
}
2025-12-02 06:54:32 +01:00
uf_done:
rava_fastframe_reset();
return !vm->had_error;
#undef UF_DISPATCH
#undef UF_PUSH
#undef UF_POP
#undef UF_PEEK
}
#endif
2025-12-03 14:08:55 +01:00
static bool _rava_vm_run_static_initializers(RavaVM_t *vm) {
for (size_t c = 0; c < vm->program->class_count; c++) {
RavaClass_t *cls = vm->program->classes[c];
for (size_t m = 0; m < cls->method_count; m++) {
RavaMethod_t *method = cls->methods[m];
if (strcmp(method->name, "<clinit>") == 0) {
RavaCallFrame_t *frame = rava_call_frame_create(method);
rava_call_stack_push(vm->call_stack, frame);
#ifdef __GNUC__
if (!_rava_vm_execute_fast(vm, frame)) {
return false;
}
#else
while (frame->pc < frame->method->instructions->count) {
RavaInstruction_t *instr = &frame->method->instructions->instructions[frame->pc];
if (instr->opcode == RAVA_OP_RETURN || instr->opcode == RAVA_OP_RETURN_VOID) {
break;
}
frame->pc++;
if (!_rava_vm_execute_instruction(vm, frame, instr)) {
return false;
}
}
#endif
rava_call_stack_pop(vm->call_stack);
rava_call_frame_destroy(frame);
}
}
}
return true;
}
2025-12-02 06:54:32 +01:00
bool rava_vm_execute(RavaVM_t *vm, const char *class_name, const char *method_name) {
2025-12-03 14:08:55 +01:00
if (!_rava_vm_run_static_initializers(vm)) {
return false;
}
2025-12-02 06:54:32 +01:00
RavaMethod_t *method = _rava_vm_find_method_cached(vm, class_name, method_name);
if (!method) {
vm->had_error = true;
2025-12-04 06:14:22 +01:00
vm->error_message = malloc(RAVA_ERROR_BUFFER_SIZE);
snprintf(vm->error_message, RAVA_ERROR_BUFFER_SIZE, "Method not found: %s.%s", class_name, method_name);
2025-12-02 06:54:32 +01:00
return false;
}
RavaCallFrame_t *frame = rava_call_frame_create(method);
rava_call_stack_push(vm->call_stack, frame);
#ifdef __GNUC__
return _rava_vm_execute_ultrafast(vm, method);
#else
while (frame->pc < frame->method->instructions->count) {
RavaInstruction_t *instr = &frame->method->instructions->instructions[frame->pc];
if (instr->opcode == RAVA_OP_RETURN || instr->opcode == RAVA_OP_RETURN_VOID) {
break;
}
frame->pc++;
if (!_rava_vm_execute_instruction(vm, frame, instr)) {
return false;
}
}
return !vm->had_error;
#endif
}
RavaValue_t rava_vm_get_result(RavaVM_t *vm) {
RavaCallFrame_t *frame = rava_call_stack_current(vm->call_stack);
if (frame && !rava_stack_is_empty(frame->operand_stack)) {
return rava_stack_peek(frame->operand_stack);
}
return rava_value_int(0);
}
2025-12-04 23:51:05 +01:00
RavaNativeValue_t rava_value_to_native(RavaValue_t value) {
RavaNativeValue_t native;
switch (value.type) {
case RAVA_VAL_INT:
native.type = RAVA_NATIVE_INT;
native.data.int_val = value.data.int_val;
break;
case RAVA_VAL_LONG:
native.type = RAVA_NATIVE_LONG;
native.data.long_val = value.data.long_val;
break;
case RAVA_VAL_FLOAT:
case RAVA_VAL_DOUBLE:
native.type = RAVA_NATIVE_DOUBLE;
native.data.double_val = (value.type == RAVA_VAL_FLOAT) ?
(double)value.data.float_val : value.data.double_val;
break;
case RAVA_VAL_BOOLEAN:
native.type = RAVA_NATIVE_BOOLEAN;
native.data.bool_val = value.data.bool_val;
break;
case RAVA_VAL_STRING:
native.type = RAVA_NATIVE_STRING;
native.data.string_val = value.data.string_val;
break;
case RAVA_VAL_OBJECT:
native.type = RAVA_NATIVE_OBJECT;
native.data.object_val = value.data.object_val;
break;
case RAVA_VAL_ARRAY:
native.type = RAVA_NATIVE_ARRAY;
native.data.array_val = value.data.array_val;
break;
default:
native.type = RAVA_NATIVE_VOID;
break;
}
return native;
}
RavaValue_t rava_native_to_value(RavaNativeValue_t native) {
RavaValue_t value;
switch (native.type) {
case RAVA_NATIVE_INT:
value = rava_value_int(native.data.int_val);
break;
case RAVA_NATIVE_LONG:
value = rava_value_long(native.data.long_val);
break;
case RAVA_NATIVE_DOUBLE:
value = rava_value_double(native.data.double_val);
break;
case RAVA_NATIVE_BOOLEAN:
value = rava_value_boolean(native.data.bool_val);
break;
case RAVA_NATIVE_STRING:
value = rava_value_string(native.data.string_val);
break;
case RAVA_NATIVE_OBJECT:
value.type = RAVA_VAL_OBJECT;
value.data.object_val = native.data.object_val;
break;
case RAVA_NATIVE_ARRAY:
value.type = RAVA_VAL_ARRAY;
value.data.array_val = native.data.array_val;
break;
default:
value = rava_value_null();
break;
}
return value;
}
bool rava_vm_load_native_library(RavaVM_t *vm, const char *path) {
if (!vm || !vm->native_registry || !path) return false;
RavaNativeLibrary_t *lib = rava_native_library_load(vm->native_registry, path);
return lib != NULL;
}
bool rava_vm_register_native(RavaVM_t *vm, const char *class_name, const char *method_name,
RavaNativeType_e return_type, RavaNativeType_e *param_types,
size_t param_count, RavaNativeFunction_fn function, void *user_data) {
if (!vm || !vm->native_registry) return false;
return rava_native_register_method(vm->native_registry, class_name, method_name,
return_type, param_types, param_count, function, user_data);
}
RavaValue_t rava_vm_call_native(RavaVM_t *vm, const char *class_name, const char *method_name,
RavaValue_t *args, size_t arg_count) {
if (!vm || !vm->native_registry) return rava_value_null();
RavaNativeMethod_t *method = rava_native_find_method(vm->native_registry, class_name, method_name);
if (!method) {
vm->had_error = true;
vm->error_message = malloc(256);
snprintf(vm->error_message, 256, "Native method not found: %s.%s", class_name, method_name);
return rava_value_null();
}
RavaNativeValue_t native_args[RAVA_MAX_METHOD_PARAMS];
for (size_t i = 0; i < arg_count && i < RAVA_MAX_METHOD_PARAMS; i++) {
native_args[i] = rava_value_to_native(args[i]);
}
RavaNativeValue_t result = rava_native_invoke(method, native_args, arg_count);
return rava_native_to_value(result);
}