This commit is contained in:
retoor 2025-12-05 01:12:39 +01:00
parent e33fa20c60
commit b46f0d9ff4
11 changed files with 945 additions and 0 deletions

139
runtime/gc/gc.c Normal file
View File

@ -0,0 +1,139 @@
#include "gc.h"
#include "gc_heap.h"
#include "gc_mark.h"
#include "gc_sweep.h"
#include "gc_roots.h"
#include "../runtime.h"
#include <stdio.h>
#include <string.h>
static RavaVM_t *g_current_vm = NULL;
static RavaGCStats_t g_stats;
void rava_gc_init(void) {
rava_gc_heap_init();
rava_gc_mark_init();
memset(&g_stats, 0, sizeof(g_stats));
g_current_vm = NULL;
}
void rava_gc_shutdown(void) {
RavaGCHeap_t *heap = rava_gc_get_heap();
RavaGCHeader_t *obj = heap->all_objects;
while (obj) {
RavaGCHeader_t *next = obj->gc_next;
rava_gc_finalize_object(obj);
obj = next;
}
heap->all_objects = NULL;
rava_gc_heap_shutdown();
}
void rava_gc_set_vm(RavaVM_t *vm) {
g_current_vm = vm;
}
void* rava_gc_alloc(size_t size, uint8_t type) {
RavaGCState_t *state = rava_gc_get_state();
if (state->gc_enabled && !state->gc_active) {
rava_gc_collect_if_needed();
}
void *ptr = rava_gc_heap_alloc(size, type);
if (ptr) {
g_stats.objects_allocated++;
g_stats.bytes_allocated += size;
g_stats.current_live_objects++;
g_stats.current_live_bytes += size;
if (g_stats.current_live_bytes > g_stats.peak_live_bytes) {
g_stats.peak_live_bytes = g_stats.current_live_bytes;
}
}
return ptr;
}
void rava_gc_free(void *ptr) {
(void)ptr;
}
void rava_gc_collect(void) {
RavaGCState_t *state = rava_gc_get_state();
if (state->gc_active) {
return;
}
state->gc_active = true;
state->objects_marked = 0;
state->bytes_marked = 0;
state->gray_count = 0;
rava_gc_scan_roots(g_current_vm);
rava_gc_process_gray_stack();
size_t live_before = g_stats.current_live_objects;
rava_gc_sweep();
rava_gc_recycle_blocks();
size_t freed = live_before - state->objects_marked;
g_stats.objects_freed += freed;
g_stats.current_live_objects = state->objects_marked;
g_stats.current_live_bytes = state->bytes_marked;
g_stats.total_collections++;
state->gc_active = false;
}
void rava_gc_collect_if_needed(void) {
RavaGCHeap_t *heap = rava_gc_get_heap();
if (heap->total_allocated > heap->gc_threshold) {
rava_gc_collect();
}
}
void rava_gc_enable(void) {
RavaGCState_t *state = rava_gc_get_state();
state->gc_enabled = true;
}
void rava_gc_disable(void) {
RavaGCState_t *state = rava_gc_get_state();
state->gc_enabled = false;
}
bool rava_gc_is_enabled(void) {
RavaGCState_t *state = rava_gc_get_state();
return state->gc_enabled;
}
void rava_gc_pin(void *ptr) {
if (!ptr) return;
RavaGCHeader_t *header = (RavaGCHeader_t*)ptr;
header->gc_flags |= RAVA_GC_FLAG_PINNED;
}
void rava_gc_unpin(void *ptr) {
if (!ptr) return;
RavaGCHeader_t *header = (RavaGCHeader_t*)ptr;
header->gc_flags &= ~RAVA_GC_FLAG_PINNED;
}
RavaGCStats_t rava_gc_get_stats(void) {
return g_stats;
}
void rava_gc_print_stats(void) {
printf("GC Statistics:\n");
printf(" Total collections: %zu\n", g_stats.total_collections);
printf(" Objects allocated: %zu\n", g_stats.objects_allocated);
printf(" Objects freed: %zu\n", g_stats.objects_freed);
printf(" Current live objects: %zu\n", g_stats.current_live_objects);
printf(" Current live bytes: %zu\n", g_stats.current_live_bytes);
printf(" Peak live bytes: %zu\n", g_stats.peak_live_bytes);
}

85
runtime/gc/gc.h Normal file
View File

@ -0,0 +1,85 @@
#ifndef RAVA_GC_H
#define RAVA_GC_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define RAVA_GC_BLOCK_SIZE (32 * 1024)
#define RAVA_GC_LINE_SIZE 128
#define RAVA_GC_LINES_PER_BLOCK (RAVA_GC_BLOCK_SIZE / RAVA_GC_LINE_SIZE)
#define RAVA_GC_GRAY_STACK_SIZE 4096
#define RAVA_GC_INITIAL_THRESHOLD (1024 * 1024)
#define RAVA_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
struct RavaGCHeader;
typedef struct RavaGCHeader RavaGCHeader_t;
typedef struct RavaGCBlock {
uint8_t line_marks[RAVA_GC_LINES_PER_BLOCK];
uint8_t flags;
uint16_t free_lines;
struct RavaGCBlock *next;
struct RavaGCBlock *prev;
uint8_t data[RAVA_GC_BLOCK_SIZE];
} RavaGCBlock_t;
#define RAVA_GC_BLOCK_FLAG_EVACUATE 0x01
#define RAVA_GC_BLOCK_FLAG_FULL 0x02
typedef struct RavaGCHeap {
RavaGCBlock_t *all_blocks;
RavaGCBlock_t *current_block;
uint8_t *bump_ptr;
uint8_t *block_limit;
RavaGCBlock_t *free_blocks;
RavaGCBlock_t *recyclable_blocks;
size_t total_allocated;
size_t live_bytes;
size_t gc_threshold;
RavaGCHeader_t *all_objects;
} RavaGCHeap_t;
typedef struct RavaGCStats {
size_t total_collections;
size_t objects_allocated;
size_t objects_freed;
size_t bytes_allocated;
size_t bytes_freed;
size_t current_live_objects;
size_t current_live_bytes;
size_t peak_live_bytes;
} RavaGCStats_t;
typedef struct RavaGCState {
RavaGCHeader_t *gray_stack[RAVA_GC_GRAY_STACK_SIZE];
size_t gray_count;
bool gc_active;
bool gc_enabled;
size_t objects_marked;
size_t bytes_marked;
} RavaGCState_t;
void rava_gc_init(void);
void rava_gc_shutdown(void);
void* rava_gc_alloc(size_t size, uint8_t type);
void rava_gc_free(void *ptr);
void rava_gc_collect(void);
void rava_gc_collect_if_needed(void);
void rava_gc_enable(void);
void rava_gc_disable(void);
bool rava_gc_is_enabled(void);
void rava_gc_pin(void *ptr);
void rava_gc_unpin(void *ptr);
RavaGCStats_t rava_gc_get_stats(void);
void rava_gc_print_stats(void);
#endif

188
runtime/gc/gc_heap.c Normal file
View File

@ -0,0 +1,188 @@
#include "gc_heap.h"
#include "../runtime.h"
#include <stdlib.h>
#include <string.h>
static RavaGCHeap_t g_heap;
void rava_gc_heap_init(void) {
memset(&g_heap, 0, sizeof(g_heap));
g_heap.gc_threshold = RAVA_GC_INITIAL_THRESHOLD;
g_heap.all_blocks = NULL;
g_heap.current_block = NULL;
g_heap.free_blocks = NULL;
g_heap.recyclable_blocks = NULL;
g_heap.all_objects = NULL;
}
void rava_gc_heap_shutdown(void) {
RavaGCBlock_t *block = g_heap.all_blocks;
while (block) {
RavaGCBlock_t *next = block->next;
free(block);
block = next;
}
block = g_heap.free_blocks;
while (block) {
RavaGCBlock_t *next = block->next;
free(block);
block = next;
}
memset(&g_heap, 0, sizeof(g_heap));
}
static RavaGCBlock_t* _rava_gc_create_block(void) {
RavaGCBlock_t *block = calloc(1, sizeof(RavaGCBlock_t));
if (!block) return NULL;
block->flags = 0;
block->free_lines = RAVA_GC_LINES_PER_BLOCK;
memset(block->line_marks, 0, sizeof(block->line_marks));
return block;
}
bool rava_gc_request_block(void) {
if (g_heap.free_blocks) {
RavaGCBlock_t *block = g_heap.free_blocks;
g_heap.free_blocks = block->next;
block->next = g_heap.all_blocks;
block->prev = NULL;
if (g_heap.all_blocks) {
g_heap.all_blocks->prev = block;
}
g_heap.all_blocks = block;
g_heap.current_block = block;
g_heap.bump_ptr = block->data;
g_heap.block_limit = block->data + RAVA_GC_BLOCK_SIZE;
return true;
}
if (g_heap.recyclable_blocks) {
RavaGCBlock_t *block = g_heap.recyclable_blocks;
g_heap.recyclable_blocks = block->next;
block->next = g_heap.all_blocks;
block->prev = NULL;
if (g_heap.all_blocks) {
g_heap.all_blocks->prev = block;
}
g_heap.all_blocks = block;
g_heap.current_block = block;
g_heap.bump_ptr = block->data;
g_heap.block_limit = block->data + RAVA_GC_BLOCK_SIZE;
return true;
}
RavaGCBlock_t *block = _rava_gc_create_block();
if (!block) return false;
block->next = g_heap.all_blocks;
block->prev = NULL;
if (g_heap.all_blocks) {
g_heap.all_blocks->prev = block;
}
g_heap.all_blocks = block;
g_heap.current_block = block;
g_heap.bump_ptr = block->data;
g_heap.block_limit = block->data + RAVA_GC_BLOCK_SIZE;
return true;
}
void* rava_gc_heap_alloc(size_t size, uint8_t type) {
size = RAVA_ALIGN(size, 8);
if (size > RAVA_GC_BLOCK_SIZE) {
RavaGCHeader_t *obj = calloc(1, size);
if (!obj) return NULL;
obj->gc_type = type;
obj->gc_mark = RAVA_GC_WHITE;
obj->gc_age = 0;
obj->gc_flags = RAVA_GC_FLAG_PINNED;
obj->gc_size = (uint32_t)size;
obj->gc_next = g_heap.all_objects;
g_heap.all_objects = obj;
g_heap.total_allocated += size;
return obj;
}
if (!g_heap.current_block || g_heap.bump_ptr + size > g_heap.block_limit) {
if (!rava_gc_request_block()) {
return NULL;
}
}
RavaGCHeader_t *obj = (RavaGCHeader_t*)g_heap.bump_ptr;
g_heap.bump_ptr += size;
g_heap.total_allocated += size;
obj->gc_type = type;
obj->gc_mark = RAVA_GC_WHITE;
obj->gc_age = 0;
obj->gc_flags = 0;
obj->gc_size = (uint32_t)size;
obj->gc_next = g_heap.all_objects;
g_heap.all_objects = obj;
return obj;
}
void rava_gc_heap_free(void *ptr, size_t size) {
(void)ptr;
(void)size;
}
void rava_gc_recycle_blocks(void) {
RavaGCBlock_t *block = g_heap.all_blocks;
RavaGCBlock_t *prev = NULL;
while (block) {
RavaGCBlock_t *next = block->next;
bool has_live = false;
for (size_t i = 0; i < RAVA_GC_LINES_PER_BLOCK; i++) {
if (block->line_marks[i]) {
has_live = true;
break;
}
}
if (!has_live && block != g_heap.current_block) {
if (prev) {
prev->next = next;
} else {
g_heap.all_blocks = next;
}
if (next) {
next->prev = prev;
}
block->next = g_heap.free_blocks;
block->prev = NULL;
g_heap.free_blocks = block;
memset(block->line_marks, 0, sizeof(block->line_marks));
block->flags = 0;
block->free_lines = RAVA_GC_LINES_PER_BLOCK;
} else {
prev = block;
}
block = next;
}
}
RavaGCHeap_t* rava_gc_get_heap(void) {
return &g_heap;
}

17
runtime/gc/gc_heap.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef RAVA_GC_HEAP_H
#define RAVA_GC_HEAP_H
#include "gc.h"
void rava_gc_heap_init(void);
void rava_gc_heap_shutdown(void);
void* rava_gc_heap_alloc(size_t size, uint8_t type);
void rava_gc_heap_free(void *ptr, size_t size);
bool rava_gc_request_block(void);
void rava_gc_recycle_blocks(void);
RavaGCHeap_t* rava_gc_get_heap(void);
#endif

108
runtime/gc/gc_mark.c Normal file
View File

@ -0,0 +1,108 @@
#include "gc_mark.h"
#include "gc_roots.h"
#include "../runtime.h"
#include <string.h>
static RavaGCState_t g_gc_state;
void rava_gc_mark_init(void) {
memset(&g_gc_state, 0, sizeof(g_gc_state));
g_gc_state.gc_enabled = true;
}
RavaGCState_t* rava_gc_get_state(void) {
return &g_gc_state;
}
void rava_gc_mark_object(RavaGCHeader_t *obj) {
if (!obj || obj->gc_mark != RAVA_GC_WHITE) {
return;
}
obj->gc_mark = RAVA_GC_GRAY;
if (g_gc_state.gray_count < RAVA_GC_GRAY_STACK_SIZE) {
g_gc_state.gray_stack[g_gc_state.gray_count++] = obj;
} else {
rava_gc_process_object(obj);
}
}
static void _rava_gc_trace_object(RavaObject_t *obj) {
for (size_t i = 0; i < obj->field_count; i++) {
rava_gc_mark_value(obj->field_values[i]);
}
}
static void _rava_gc_trace_array(RavaArray_t *arr) {
if (arr->element_type == RAVA_VAL_OBJECT ||
arr->element_type == RAVA_VAL_ARRAY) {
RavaValue_t *values = (RavaValue_t*)arr->data;
for (size_t i = 0; i < arr->length; i++) {
rava_gc_mark_value(values[i]);
}
}
}
static void _rava_gc_trace_arraylist(RavaArrayList_t *list) {
for (size_t i = 0; i < list->size; i++) {
rava_gc_mark_value(list->data[i]);
}
}
static void _rava_gc_trace_hashmap(RavaHashMap_t *map) {
for (size_t i = 0; i < map->bucket_count; i++) {
if (map->buckets[i].occupied) {
rava_gc_mark_value(map->buckets[i].value);
}
}
}
static void _rava_gc_trace_methodref(RavaMethodRef_t *ref) {
if (ref->has_target) {
rava_gc_mark_value(ref->target);
}
}
void rava_gc_process_object(RavaGCHeader_t *obj) {
obj->gc_mark = RAVA_GC_BLACK;
g_gc_state.objects_marked++;
g_gc_state.bytes_marked += obj->gc_size;
switch (obj->gc_type) {
case RAVA_GC_TYPE_OBJECT: {
RavaObject_t *o = (RavaObject_t*)obj;
_rava_gc_trace_object(o);
break;
}
case RAVA_GC_TYPE_ARRAY: {
RavaArray_t *a = (RavaArray_t*)obj;
_rava_gc_trace_array(a);
break;
}
case RAVA_GC_TYPE_ARRAYLIST: {
RavaArrayList_t *l = (RavaArrayList_t*)obj;
_rava_gc_trace_arraylist(l);
break;
}
case RAVA_GC_TYPE_HASHMAP: {
RavaHashMap_t *m = (RavaHashMap_t*)obj;
_rava_gc_trace_hashmap(m);
break;
}
case RAVA_GC_TYPE_METHODREF: {
RavaMethodRef_t *r = (RavaMethodRef_t*)obj;
_rava_gc_trace_methodref(r);
break;
}
default:
break;
}
}
void rava_gc_process_gray_stack(void) {
while (g_gc_state.gray_count > 0) {
RavaGCHeader_t *obj = g_gc_state.gray_stack[--g_gc_state.gray_count];
rava_gc_process_object(obj);
}
}

13
runtime/gc/gc_mark.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef RAVA_GC_MARK_H
#define RAVA_GC_MARK_H
#include "gc.h"
void rava_gc_mark_init(void);
void rava_gc_mark_object(RavaGCHeader_t *obj);
void rava_gc_process_gray_stack(void);
void rava_gc_process_object(RavaGCHeader_t *obj);
RavaGCState_t* rava_gc_get_state(void);
#endif

126
runtime/gc/gc_roots.c Normal file
View File

@ -0,0 +1,126 @@
#include "gc_roots.h"
#include "gc_mark.h"
#include "../runtime.h"
#include "../fastframe.h"
#include "../nanbox.h"
void rava_gc_mark_value(RavaValue_t value) {
switch (value.type) {
case RAVA_VAL_OBJECT:
if (value.data.object_val) {
rava_gc_mark_object(&value.data.object_val->gc);
}
break;
case RAVA_VAL_ARRAY:
if (value.data.array_val) {
rava_gc_mark_object(&value.data.array_val->gc);
}
break;
case RAVA_VAL_ARRAYLIST:
if (value.data.arraylist_val) {
rava_gc_mark_object(&value.data.arraylist_val->gc);
}
break;
case RAVA_VAL_HASHMAP:
if (value.data.hashmap_val) {
rava_gc_mark_object(&value.data.hashmap_val->gc);
}
break;
case RAVA_VAL_SOCKET:
if (value.data.socket_val) {
rava_gc_mark_object(&value.data.socket_val->gc);
}
break;
case RAVA_VAL_STREAM:
if (value.data.stream_val) {
rava_gc_mark_object(&value.data.stream_val->gc);
}
break;
case RAVA_VAL_METHOD_REF:
if (value.data.method_ref_val) {
rava_gc_mark_object(&value.data.method_ref_val->gc);
}
break;
default:
break;
}
}
void rava_gc_mark_nanbox(uint64_t v) {
if (rava_nanbox_is_object(v)) {
void *ptr = rava_nanbox_as_object(v);
if (ptr) {
RavaObject_t *obj = (RavaObject_t*)ptr;
rava_gc_mark_object(&obj->gc);
}
} else if (rava_nanbox_is_array(v)) {
void *ptr = rava_nanbox_as_array(v);
if (ptr) {
RavaArray_t *arr = (RavaArray_t*)ptr;
rava_gc_mark_object(&arr->gc);
}
}
}
void rava_gc_mark_string(const char *str) {
(void)str;
}
void rava_gc_scan_roots(RavaVM_t *vm) {
if (!vm) return;
if (vm->call_stack) {
for (size_t i = 0; i < vm->call_stack->count; i++) {
RavaCallFrame_t *frame = vm->call_stack->frames[i];
if (!frame) continue;
for (size_t j = 0; j < frame->local_count; j++) {
rava_gc_mark_value(frame->locals[j]);
}
if (frame->operand_stack) {
for (size_t j = 0; j < frame->operand_stack->top; j++) {
rava_gc_mark_value(frame->operand_stack->values[j]);
}
}
if (frame->has_this) {
rava_gc_mark_value(frame->this_ref);
}
}
}
extern size_t rava_frame_depth;
extern FastFrame_t rava_frame_pool[];
for (size_t i = 0; i < rava_frame_depth; i++) {
FastFrame_t *frame = &rava_frame_pool[i];
for (size_t j = 0; j < RAVA_MAX_LOCALS_FIXED; j++) {
rava_gc_mark_nanbox(frame->locals[j]);
}
for (size_t j = 0; j < frame->stack_top; j++) {
rava_gc_mark_nanbox(frame->stack[j]);
}
if (frame->has_this) {
rava_gc_mark_nanbox(frame->this_ref);
}
}
if (vm->static_fields) {
for (size_t i = 0; i < vm->static_fields->count; i++) {
rava_gc_mark_value(vm->static_fields->fields[i].value);
}
}
for (size_t i = 0; i < RAVA_INTERN_TABLE_SIZE; i++) {
if (vm->intern_table.strings[i]) {
rava_gc_mark_string(vm->intern_table.strings[i]);
}
}
if (vm->has_exception) {
rava_gc_mark_value(vm->exception_value);
}
}

12
runtime/gc/gc_roots.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef RAVA_GC_ROOTS_H
#define RAVA_GC_ROOTS_H
#include "gc.h"
#include "../runtime.h"
void rava_gc_scan_roots(RavaVM_t *vm);
void rava_gc_mark_value(RavaValue_t value);
void rava_gc_mark_nanbox(uint64_t v);
void rava_gc_mark_string(const char *str);
#endif

114
runtime/gc/gc_sweep.c Normal file
View File

@ -0,0 +1,114 @@
#include "gc_sweep.h"
#include "gc_heap.h"
#include "../runtime.h"
#include <stdlib.h>
#include <unistd.h>
static void _rava_gc_finalize_object_data(RavaObject_t *o) {
free(o->class_name);
for (size_t i = 0; i < o->field_count; i++) {
free(o->field_names[i]);
}
free(o->field_names);
free(o->field_values);
}
static void _rava_gc_finalize_array_data(RavaArray_t *a) {
free(a->data);
}
static void _rava_gc_finalize_arraylist_data(RavaArrayList_t *l) {
free(l->data);
}
static void _rava_gc_finalize_hashmap_data(RavaHashMap_t *m) {
for (size_t i = 0; i < m->bucket_count; i++) {
if (m->buckets[i].occupied) {
free(m->buckets[i].key);
}
}
free(m->buckets);
}
static void _rava_gc_finalize_socket_data(RavaSocket_t *s) {
if (!s->closed && s->fd >= 0) {
close(s->fd);
}
}
static void _rava_gc_finalize_methodref_data(RavaMethodRef_t *r) {
free(r->class_name);
free(r->method_name);
}
void rava_gc_finalize_object(RavaGCHeader_t *obj) {
switch (obj->gc_type) {
case RAVA_GC_TYPE_OBJECT: {
RavaObject_t *o = (RavaObject_t*)obj;
_rava_gc_finalize_object_data(o);
break;
}
case RAVA_GC_TYPE_ARRAY: {
RavaArray_t *a = (RavaArray_t*)obj;
_rava_gc_finalize_array_data(a);
break;
}
case RAVA_GC_TYPE_ARRAYLIST: {
RavaArrayList_t *l = (RavaArrayList_t*)obj;
_rava_gc_finalize_arraylist_data(l);
break;
}
case RAVA_GC_TYPE_HASHMAP: {
RavaHashMap_t *m = (RavaHashMap_t*)obj;
_rava_gc_finalize_hashmap_data(m);
break;
}
case RAVA_GC_TYPE_SOCKET: {
RavaSocket_t *s = (RavaSocket_t*)obj;
_rava_gc_finalize_socket_data(s);
break;
}
case RAVA_GC_TYPE_METHODREF: {
RavaMethodRef_t *r = (RavaMethodRef_t*)obj;
_rava_gc_finalize_methodref_data(r);
break;
}
default:
break;
}
if (obj->gc_flags & RAVA_GC_FLAG_PINNED) {
free(obj);
}
}
void rava_gc_sweep(void) {
RavaGCHeap_t *heap = rava_gc_get_heap();
RavaGCHeader_t **ptr = &heap->all_objects;
size_t freed_count = 0;
size_t freed_bytes = 0;
while (*ptr) {
RavaGCHeader_t *obj = *ptr;
if (obj->gc_mark == RAVA_GC_WHITE) {
*ptr = obj->gc_next;
freed_bytes += obj->gc_size;
freed_count++;
rava_gc_finalize_object(obj);
} else {
obj->gc_mark = RAVA_GC_WHITE;
obj->gc_age++;
ptr = &obj->gc_next;
}
}
heap->live_bytes = heap->total_allocated - freed_bytes;
heap->total_allocated = heap->live_bytes;
if (heap->live_bytes > 0) {
heap->gc_threshold = heap->live_bytes * 2;
} else {
heap->gc_threshold = RAVA_GC_INITIAL_THRESHOLD;
}
}

9
runtime/gc/gc_sweep.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef RAVA_GC_SWEEP_H
#define RAVA_GC_SWEEP_H
#include "gc.h"
void rava_gc_sweep(void);
void rava_gc_finalize_object(RavaGCHeader_t *obj);
#endif

134
tests/test_gc.c Normal file
View File

@ -0,0 +1,134 @@
#include "test_utils.h"
UnittestTestResult_t* test_gc_basic_allocation(void) {
UNITTEST_BEGIN_TEST("TestGC", "test_gc_basic_allocation");
const char *source =
"public class Test {\n"
" public static int main() {\n"
" int[] arr = new int[100];\n"
" for (int i = 0; i < 100; i++) {\n"
" arr[i] = i;\n"
" }\n"
" return arr[99];\n"
" }\n"
"}\n";
RAVA_TEST_RUN(_unittest_result, source, "Test", "main", 99,
"Should allocate array without crash");
UNITTEST_END_TEST();
}
UnittestTestResult_t* test_gc_object_allocation(void) {
UNITTEST_BEGIN_TEST("TestGC", "test_gc_object_allocation");
const char *source =
"public class Point {\n"
" public int x;\n"
" public int y;\n"
"}\n"
"public class Test {\n"
" public static int main() {\n"
" Point p = new Point();\n"
" p.x = 10;\n"
" p.y = 20;\n"
" return p.x + p.y;\n"
" }\n"
"}\n";
RAVA_TEST_RUN(_unittest_result, source, "Test", "main", 30,
"Should allocate object without crash");
UNITTEST_END_TEST();
}
UnittestTestResult_t* test_gc_many_allocations(void) {
UNITTEST_BEGIN_TEST("TestGC", "test_gc_many_allocations");
const char *source =
"public class Test {\n"
" public static int main() {\n"
" int sum = 0;\n"
" for (int i = 0; i < 1000; i++) {\n"
" int[] temp = new int[10];\n"
" temp[0] = i;\n"
" sum = sum + temp[0];\n"
" }\n"
" return sum;\n"
" }\n"
"}\n";
RAVA_TEST_RUN(_unittest_result, source, "Test", "main", 499500,
"Should handle many allocations");
UNITTEST_END_TEST();
}
UnittestTestResult_t* test_gc_arraylist(void) {
UNITTEST_BEGIN_TEST("TestGC", "test_gc_arraylist");
const char *source =
"public class Test {\n"
" public static int main() {\n"
" ArrayList list = new ArrayList();\n"
" for (int i = 0; i < 100; i++) {\n"
" list.add(i);\n"
" }\n"
" return list.size();\n"
" }\n"
"}\n";
RAVA_TEST_RUN(_unittest_result, source, "Test", "main", 100,
"Should handle ArrayList allocations");
UNITTEST_END_TEST();
}
UnittestTestResult_t* test_gc_hashmap(void) {
UNITTEST_BEGIN_TEST("TestGC", "test_gc_hashmap");
const char *source =
"public class Test {\n"
" public static int main() {\n"
" HashMap map = new HashMap();\n"
" map.put(\"one\", 1);\n"
" map.put(\"two\", 2);\n"
" map.put(\"three\", 3);\n"
" return map.size();\n"
" }\n"
"}\n";
RAVA_TEST_RUN(_unittest_result, source, "Test", "main", 3,
"Should handle HashMap allocations");
UNITTEST_END_TEST();
}
int main(int argc, char **argv) {
UnittestConfig_t *config = unittest_config_create();
config->verbosity = 2;
if (argc > 1 && strcmp(argv[1], "--json") == 0) {
config->output_format = UNITTEST_FORMAT_JSON;
config->use_colors = false;
}
UnittestTestSuite_t *suite = unittest_test_suite_create("GC Tests");
UnittestTestCase_t *tc = unittest_test_case_create("TestGC");
unittest_test_case_add_result(tc, test_gc_basic_allocation());
unittest_test_case_add_result(tc, test_gc_object_allocation());
unittest_test_case_add_result(tc, test_gc_many_allocations());
unittest_test_case_add_result(tc, test_gc_arraylist());
unittest_test_case_add_result(tc, test_gc_hashmap());
unittest_test_suite_add_test_case(suite, tc);
unittest_generate_report(suite, config);
int failures = suite->total_failed + suite->total_errors;
unittest_test_suite_destroy(suite);
unittest_config_destroy(config);
return failures > 0 ? 1 : 0;
}