mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
I've ripped out the deep copying, and flattened the bucket usage, but
this results in no memory being freed or reused for the lifetime of the
program.
This is shown most clearly with this script:
```toy
fn makeCounter() {
var counter: int = 0;
fn increment() {
return ++counter;
}
return increment;
}
var tally = makeCounter();
while (true) {
var result = tally();
if (result >= 10_000_000) {
break;
}
}
```
The number of calls vs amount of memory consumed is:
```
function calls -> memory used in megabytes
1_000 -> 0.138128
10_000 -> 2.235536
100_000 -> 21.7021
1_000_000 -> 216.1712
10_000_000 -> 1520.823
```
Obviously this needs to be fixed, as ballooning to gigabytes of memory
in only a moment isn't practical. That will be the next task - to find
some way to free memory that isn't needed anymore.
See #163, #160
192 lines
5.5 KiB
C
192 lines
5.5 KiB
C
#include "toy_scope.h"
|
|
#include "toy_console_colors.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "toy_print.h"
|
|
|
|
//utils
|
|
static void incrementRefCount(Toy_Scope* scope) {
|
|
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
|
//check for issues
|
|
if (iter->next != NULL && iter->next->refCount == 0) {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope's ancestor has a refcount of 0'\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
iter->refCount++;
|
|
}
|
|
}
|
|
|
|
static void decrementRefCount(Toy_Scope* scope) {
|
|
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
|
iter->refCount--;
|
|
if (iter->refCount == 0 && iter->table != NULL) {
|
|
Toy_freeTable(iter->table);
|
|
iter->table = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Toy_TableEntry* lookupScope(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) {
|
|
//terminate
|
|
if (scope == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//continue after a dummy
|
|
if (scope->table == NULL) {
|
|
return recursive ? lookupScope(scope->next, key, hash, recursive) : NULL;
|
|
}
|
|
|
|
//copy and modify the code from Toy_lookupTable, so it can behave slightly differently
|
|
unsigned int probe = hash % scope->table->capacity;
|
|
|
|
while (true) {
|
|
//found the entry
|
|
if (TOY_VALUE_IS_STRING(scope->table->data[probe].key) && Toy_compareStrings(TOY_VALUE_AS_STRING(scope->table->data[probe].key), key) == 0) {
|
|
return &(scope->table->data[probe]);
|
|
}
|
|
|
|
//if its an empty slot (didn't find it here)
|
|
if (TOY_VALUE_IS_NULL(scope->table->data[probe].key)) {
|
|
return recursive ? lookupScope(scope->next, key, hash, recursive) : NULL;
|
|
}
|
|
|
|
//adjust and continue
|
|
probe = (probe + 1) % scope->table->capacity;
|
|
}
|
|
}
|
|
|
|
//exposed functions
|
|
Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
|
Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
|
|
|
|
newScope->next = scope;
|
|
newScope->table = Toy_allocateTable();
|
|
newScope->refCount = 0;
|
|
|
|
incrementRefCount(newScope);
|
|
|
|
return newScope;
|
|
}
|
|
|
|
Toy_Scope* Toy_private_pushDummyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
|
Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
|
|
|
|
newScope->next = scope;
|
|
newScope->table = NULL;
|
|
newScope->refCount = 0;
|
|
|
|
incrementRefCount(newScope);
|
|
|
|
return newScope;
|
|
}
|
|
|
|
Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
|
if (scope == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
decrementRefCount(scope);
|
|
return scope->next;
|
|
}
|
|
|
|
void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
|
if (key->info.type != TOY_STRING_NAME) {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
if (scope->table == NULL) {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't declare in a dummy scope\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), false);
|
|
|
|
if (entryPtr != NULL) {
|
|
char buffer[key->info.length + 256];
|
|
sprintf(buffer, "Can't redefine a variable: %s", key->name.data);
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
//type check
|
|
Toy_ValueType kt = Toy_getNameStringVarType(key);
|
|
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type && value.type != TOY_VALUE_REFERENCE) {
|
|
char buffer[key->info.length + 256];
|
|
sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value);
|
|
}
|
|
|
|
void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
|
if (key->info.type != TOY_STRING_NAME) {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
|
|
if (entryPtr == NULL) {
|
|
char buffer[key->info.length + 256];
|
|
sprintf(buffer, "Undefined variable: %s\n", key->name.data);
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
//type check
|
|
Toy_ValueType kt = Toy_getNameStringVarType( TOY_VALUE_AS_STRING(entryPtr->key) );
|
|
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type && value.type != TOY_VALUE_REFERENCE) {
|
|
char buffer[key->info.length + 256];
|
|
sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
//constness check
|
|
if (Toy_getNameStringVarConstant( TOY_VALUE_AS_STRING(entryPtr->key) )) {
|
|
char buffer[key->info.length + 256];
|
|
sprintf(buffer, "Can't reassign to constant variable %s", key->name.data);
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
entryPtr->value = value;
|
|
}
|
|
|
|
Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
|
|
if (key->info.type != TOY_STRING_NAME) {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
|
|
if (entryPtr == NULL) {
|
|
char buffer[key->info.length + 256];
|
|
sprintf(buffer, "Undefined variable: %s\n", key->name.data);
|
|
Toy_error(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
return &(entryPtr->value);
|
|
}
|
|
|
|
bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key) {
|
|
if (key->info.type != TOY_STRING_NAME) {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
|
|
return entryPtr != NULL;
|
|
}
|