Found and fixed a memory leak in the rope strings
Also fixed a bit manipulation error in the GC.
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
#include "bucket_inspector.h"
|
||||||
|
// #include <toy_string.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int inspect_bucket(Toy_Bucket** bucketHandle) {
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
|
//for each bucket
|
||||||
|
for (Toy_Bucket* iter = (*bucketHandle); iter != NULL; iter = iter->next) {
|
||||||
|
int occupied = 0;
|
||||||
|
int released = 0;
|
||||||
|
unsigned char* ptr = iter->data;
|
||||||
|
|
||||||
|
|
||||||
|
while ((ptr - iter->data < iter->count) && *((int*)ptr) != 0) { //for each partition
|
||||||
|
if ( ( *((int*)ptr) & 1) == 0) { //is this partition still in use?
|
||||||
|
occupied++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
released++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//jump distance: ((*((int*)ptr) | 1) ^ 1) + 4
|
||||||
|
// printf(" jump %d, ", ((*((int*)ptr) | 1) ^ 1) + 4);
|
||||||
|
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Bucket link %d: count %u, %d occupied, %d released\n", depth, iter->count, occupied, released);
|
||||||
|
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
int inspect_bucket_for_strings(Toy_Bucket** bucketHandle) {
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
|
//for each bucket
|
||||||
|
for (Toy_Bucket* iter = (*bucketHandle); iter != NULL; iter = iter->next) {
|
||||||
|
int occupied = 0;
|
||||||
|
int released = 0;
|
||||||
|
unsigned char* ptr = iter->data;
|
||||||
|
|
||||||
|
|
||||||
|
while ((ptr - iter->data < iter->count) && *((int*)ptr) != 0) { //for each partition
|
||||||
|
if ( ( *((int*)ptr) & 1) == 0) { //is this partition still in use?
|
||||||
|
occupied++;
|
||||||
|
|
||||||
|
//try to print as a string if possible
|
||||||
|
Toy_String* str = (void*)(ptr + 4);
|
||||||
|
|
||||||
|
if (str->info.type == TOY_STRING_LEAF && str->info.length < 255) {
|
||||||
|
printf("String Leaf (%d bytes, %d refCount): %.*s\n", *((int*)ptr), str->info.refCount, str->info.length, str->leaf.data);
|
||||||
|
}
|
||||||
|
else if (str->info.type == TOY_STRING_NODE) {
|
||||||
|
printf("String Node (%d bytes, %d refCount): ...\n", *((int*)ptr), str->info.refCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
released++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//jump distance: ((*((int*)ptr) | 1) ^ 1) + 4
|
||||||
|
// printf(" jump %d, ", ((*((int*)ptr) | 1) ^ 1) + 4);
|
||||||
|
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Bucket link %d: count %u, %d occupied, %d released\n", depth, iter->count, occupied, released);
|
||||||
|
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_bucket.h"
|
||||||
|
|
||||||
|
int inspect_bucket(Toy_Bucket** bucketHandle);
|
||||||
|
// int inspect_bucket_for_strings(Toy_Bucket** bucketHandle);
|
||||||
+13
@@ -1,5 +1,6 @@
|
|||||||
#include "ast_inspector.h"
|
#include "ast_inspector.h"
|
||||||
#include "bytecode_inspector.h"
|
#include "bytecode_inspector.h"
|
||||||
|
#include "bucket_inspector.h"
|
||||||
|
|
||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
@@ -380,14 +381,26 @@ int repl(const char* filepath, bool verbose) {
|
|||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
|
|
||||||
|
int depthBeforeGC = 0;
|
||||||
|
int depthAfterGC = 0;
|
||||||
|
|
||||||
//print the debug info
|
//print the debug info
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
debugStackPrint(vm.stack);
|
debugStackPrint(vm.stack);
|
||||||
debugScopePrint(vm.scope, 0);
|
debugScopePrint(vm.scope, 0);
|
||||||
|
|
||||||
|
depthBeforeGC = inspect_bucket(&vm.memoryBucket);
|
||||||
}
|
}
|
||||||
|
|
||||||
//free the memory, and leave the VM ready for the next loop
|
//free the memory, and leave the VM ready for the next loop
|
||||||
Toy_resetVM(&vm, true, true);
|
Toy_resetVM(&vm, true, true);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
depthAfterGC = inspect_bucket(&vm.memoryBucket);
|
||||||
|
|
||||||
|
printf("GC Report: %d -> %d\n", depthBeforeGC, depthAfterGC);
|
||||||
|
}
|
||||||
|
|
||||||
free(bytecode);
|
free(bytecode);
|
||||||
|
|
||||||
printf("%s> ", prompt); //shows the terminal prompt
|
printf("%s> ", prompt); //shows the terminal prompt
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
fn swap(a, b) {
|
fn swap(a, b) {
|
||||||
return b, a;
|
return b, a;
|
||||||
}
|
}
|
||||||
@@ -10,4 +10,10 @@ var c;
|
|||||||
var d;
|
var d;
|
||||||
|
|
||||||
//BUG: still causes a segfault
|
//BUG: still causes a segfault
|
||||||
c, d = swap(a, b);
|
c, d = swap(a, b);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
{ var str = "Hello"; var i = 0; while (i < 10_000) { str = str .. " World"; i++; } }
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -88,7 +88,7 @@ TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
|
|||||||
gc = false;
|
gc = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ptr += (*((int*)(ptr)) ^ 1) + 4; //XOR to remove the 'free' flag from the size
|
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
|
||||||
}
|
}
|
||||||
|
|
||||||
//free this link, if its been entirely released
|
//free this link, if its been entirely released
|
||||||
|
|||||||
@@ -193,6 +193,8 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Toy_freeValue(entryPtr->value);
|
||||||
|
|
||||||
entryPtr->value = value;
|
entryPtr->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,18 @@ static void incrementRefCount(Toy_String* str) {
|
|||||||
static void decrementRefCount(Toy_String* str) {
|
static void decrementRefCount(Toy_String* str) {
|
||||||
str->info.refCount--;
|
str->info.refCount--;
|
||||||
if (str->info.type == TOY_STRING_NODE) {
|
if (str->info.type == TOY_STRING_NODE) {
|
||||||
|
//the parent of this node triggered a decrement across the whole tree
|
||||||
decrementRefCount(str->node.left);
|
decrementRefCount(str->node.left);
|
||||||
decrementRefCount(str->node.right);
|
decrementRefCount(str->node.right);
|
||||||
}
|
}
|
||||||
if (str->info.refCount == 0) {
|
if (str->info.refCount == 0) {
|
||||||
|
if (str->info.type == TOY_STRING_NODE) {
|
||||||
|
//THIS node has triggered the decrement, so run this again
|
||||||
|
decrementRefCount(str->node.left);
|
||||||
|
decrementRefCount(str->node.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
//mark this memory as unused
|
||||||
Toy_releaseBucketPartition((void*)str);
|
Toy_releaseBucketPartition((void*)str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -210,7 +210,7 @@ static void processAssign(Toy_VM* vm) {
|
|||||||
Toy_Value name = Toy_popStack(&vm->stack);
|
Toy_Value name = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
//assign it
|
//assign it
|
||||||
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), Toy_copyValue(&vm->memoryBucket, value)); //scope now owns the value, doesn't need to be freed
|
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), Toy_copyValue(&vm->memoryBucket, value));
|
||||||
|
|
||||||
//in case of chaining, leave a copy on the stack
|
//in case of chaining, leave a copy on the stack
|
||||||
bool chainedAssignment = READ_BYTE(vm);
|
bool chainedAssignment = READ_BYTE(vm);
|
||||||
@@ -220,6 +220,7 @@ static void processAssign(Toy_VM* vm) {
|
|||||||
|
|
||||||
//cleanup
|
//cleanup
|
||||||
Toy_freeValue(name);
|
Toy_freeValue(name);
|
||||||
|
Toy_freeValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void processAssignCompound(Toy_VM* vm) {
|
static void processAssignCompound(Toy_VM* vm) {
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ int test_string_concatenation(void) {
|
|||||||
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
Toy_freeString(result);
|
Toy_freeString(result);
|
||||||
Toy_freeString(first);
|
// Toy_freeString(first); //these do NOT need to be freed manually
|
||||||
Toy_freeString(second);
|
// Toy_freeString(second);
|
||||||
Toy_freeBucket(&bucket);
|
Toy_freeBucket(&bucket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user