|
|
@@ -6,16 +6,25 @@
|
|
|
#include <stdlib.h>
|
|
|
#endif
|
|
|
|
|
|
+#define GC_HEAP_GROW_FACTOR 2
|
|
|
+
|
|
|
+static void markArray(ValueArray *);
|
|
|
+
|
|
|
// oldSize newSize Operation
|
|
|
// 0 Non‑zero Allocate new block.
|
|
|
// Non‑zero 0 Free allocation.
|
|
|
// Non‑zero Smaller than oldSize Shrink existing allocation.
|
|
|
// Non‑zero Larger than oldSize Grow existing allocation.
|
|
|
void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
|
|
|
+ vm.bytesAllocated += newSize - oldSize;
|
|
|
if (newSize > oldSize) {
|
|
|
#ifdef DEBUG_STRESS_GC
|
|
|
collectGarbage();
|
|
|
#endif
|
|
|
+
|
|
|
+ if (vm.bytesAllocated > vm.nextGC) {
|
|
|
+ collectGarbage();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (newSize == 0) {
|
|
|
@@ -76,6 +85,8 @@ void freeObjects() {
|
|
|
freeObject(object);
|
|
|
object = next;
|
|
|
}
|
|
|
+
|
|
|
+ free(vm.grayStack);
|
|
|
}
|
|
|
static void markRoots() {
|
|
|
for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
|
|
|
@@ -86,29 +97,101 @@ static void markRoots() {
|
|
|
markObject((Obj *) vm.frames[i].closure);
|
|
|
}
|
|
|
|
|
|
- for (ObjUpvalue *upvalue = vm.openUpvalues; upvalue != NULL; upvalue = upvalue->next) {
|
|
|
+ for (ObjUpvalue *upvalue = vm.openUpvalues;
|
|
|
+ upvalue != NULL; upvalue = upvalue->next) {
|
|
|
markObject((Obj *) upvalue);
|
|
|
}
|
|
|
|
|
|
markTable(&vm.globals);
|
|
|
markCompilerRoots();
|
|
|
}
|
|
|
+void blackenObject(Obj *object) {
|
|
|
+#ifdef DEBUG_LOG_GC
|
|
|
+ printf("%p blacken ", (void *) object);
|
|
|
+ printValue(OBJ_VAL(object));
|
|
|
+ printf("\n");
|
|
|
+#endif
|
|
|
+
|
|
|
+ switch (object->type) {
|
|
|
+ case OBJ_CLOSURE: {
|
|
|
+ ObjClosure *closure = (ObjClosure *) object;
|
|
|
+ markObject((Obj *) closure->function);
|
|
|
+ for (int i = 0; i < closure->upvalueCount; i++) {
|
|
|
+ markObject((Obj *) closure->upvalues[i]);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case OBJ_FUNCTION: {
|
|
|
+ ObjFunction *function = (ObjFunction *) object;
|
|
|
+ markObject((Obj *) function->name);
|
|
|
+ markArray(&function->chunk.constants);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case OBJ_UPVALUE:
|
|
|
+ markValue(((ObjUpvalue *) object)->closed);
|
|
|
+ break;
|
|
|
+ case OBJ_NATIVE:
|
|
|
+ case OBJ_STRING:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+void traceReferences() {
|
|
|
+ while (vm.grayCount > 0) {
|
|
|
+ Obj *object = vm.grayStack[--vm.grayCount];
|
|
|
+ blackenObject(object);
|
|
|
+ }
|
|
|
+}
|
|
|
+void sweep() {
|
|
|
+ Obj *previous = NULL;
|
|
|
+ Obj *object = vm.objects;
|
|
|
+ while (object != NULL) {
|
|
|
+ if (object->isMarked) {
|
|
|
+ object->isMarked = false;
|
|
|
+ previous = object;
|
|
|
+ object = object->next;
|
|
|
+ } else {
|
|
|
+ Obj *unreached = object;
|
|
|
+ object = object->next;
|
|
|
+ if (previous != NULL) {
|
|
|
+ previous->next = object;
|
|
|
+ } else {
|
|
|
+ vm.objects = object;
|
|
|
+ }
|
|
|
+
|
|
|
+ freeObject(unreached);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
void collectGarbage() {
|
|
|
#ifdef DEBUG_LOG_GC
|
|
|
printf("-- gc begin\n");
|
|
|
+ size_t before = vm.bytesAllocated;
|
|
|
#endif
|
|
|
|
|
|
markRoots();
|
|
|
+ traceReferences();
|
|
|
+ tableRemoveWhite(&vm.strings);
|
|
|
+ sweep();// sweeping unused objects
|
|
|
+
|
|
|
+ vm.nextGC = vm.bytesAllocated * GC_HEAP_GROW_FACTOR;
|
|
|
|
|
|
#ifdef DEBUG_LOG_GC
|
|
|
printf("-- gc end\n");
|
|
|
+ printf(" collected %zu bytes (from %zu to %zu) next at %zu\n",
|
|
|
+ before - vm.bytesAllocated, before, vm.bytesAllocated, vm.nextGC);
|
|
|
#endif
|
|
|
}
|
|
|
void markValue(Value value) {
|
|
|
if (IS_OBJ(value)) markObject(AS_OBJ(value));
|
|
|
}
|
|
|
+static void markArray(ValueArray *array) {
|
|
|
+ for (int i = 0; i < array->count; i++) {
|
|
|
+ markValue(array->values[i]);
|
|
|
+ }
|
|
|
+}
|
|
|
void markObject(Obj *object) {
|
|
|
if (object == NULL) return;
|
|
|
+ if (object->isMarked) return;
|
|
|
#ifdef DEBUG_LOG_GC
|
|
|
printf("%p mark ", (void *) object);
|
|
|
printValue(OBJ_VAL(object));
|
|
|
@@ -116,4 +199,13 @@ void markObject(Obj *object) {
|
|
|
#endif
|
|
|
|
|
|
object->isMarked = true;
|
|
|
+
|
|
|
+ if (vm.grayCapacity < vm.grayCount + 1) {
|
|
|
+ vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
|
|
|
+ vm.grayStack = (Obj **) realloc(vm.grayStack, sizeof(Obj *) * vm.grayCapacity);
|
|
|
+
|
|
|
+ if (vm.grayStack == NULL) exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.grayStack[vm.grayCount++] = object;
|
|
|
}
|