Initial check-in.
This commit is contained in:
commit
e0a2599d8d
|
@ -0,0 +1,18 @@
|
|||
*#
|
||||
.#*
|
||||
*~
|
||||
*.orig
|
||||
npm-debug.log
|
||||
package.yml
|
||||
node_modules/*
|
||||
tmp/
|
||||
bin/_mocha
|
||||
bin/mocha
|
||||
bin/escodegen
|
||||
bin/esgenerate
|
||||
test-reports.xml
|
||||
LisperatorLanguage
|
||||
src/test.js
|
||||
src/test.coffee
|
||||
notes/
|
||||
build/
|
|
@ -0,0 +1,7 @@
|
|||
project("Collector")
|
||||
|
||||
list(APPEND CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS} -g -ftest-coverage -fprofile-arcs")
|
||||
|
||||
add_executable(collector
|
||||
src/collector.c)
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_STACK 256
|
||||
#define MAX_BARRIER 8
|
||||
|
||||
typedef enum {
|
||||
OBJ_INT,
|
||||
OBJ_PAIR
|
||||
} ObjectType;
|
||||
|
||||
typedef struct sObject {
|
||||
unsigned char marked;
|
||||
struct sObject* next;
|
||||
ObjectType type;
|
||||
union {
|
||||
int value;
|
||||
struct {
|
||||
struct sObject* head;
|
||||
struct sObject* tail;
|
||||
};
|
||||
};
|
||||
} Object;
|
||||
|
||||
typedef struct {
|
||||
Object* stack[MAX_STACK];
|
||||
Object* root;
|
||||
int stackSize;
|
||||
int numObjects;
|
||||
int maxObjects;
|
||||
} VM;
|
||||
|
||||
void assert(int condition, const char* message) {
|
||||
if (!condition) {
|
||||
printf("%s\n", message);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
VM* newVM() {
|
||||
VM* vm = malloc(sizeof(VM));
|
||||
vm->stackSize = 0;
|
||||
vm->numObjects = 0;
|
||||
vm->maxObjects = MAX_BARRIER;
|
||||
return vm;
|
||||
}
|
||||
|
||||
void push(VM* vm, Object* value) {
|
||||
assert(vm->stackSize < MAX_STACK, "Stack overflow!");
|
||||
vm->stack[vm->stackSize++] = value;
|
||||
}
|
||||
|
||||
Object* pop(VM* vm) {
|
||||
assert(vm->stackSize > 0, "Stack underflow!");
|
||||
return vm->stack[--(vm->stackSize)];
|
||||
}
|
||||
|
||||
void mark(Object* object) {
|
||||
if (object->marked) {
|
||||
return;
|
||||
}
|
||||
|
||||
object->marked = 1;
|
||||
if (object->type == OBJ_PAIR) {
|
||||
mark(object->head);
|
||||
mark(object->tail);
|
||||
}
|
||||
}
|
||||
|
||||
void markAll(VM* vm) {
|
||||
for(int i = 0; i < vm->stackSize; i++) {
|
||||
mark(vm->stack[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void sweep(VM* vm) {
|
||||
Object** object = &vm->root;
|
||||
while (*object) {
|
||||
if (!(*object)->marked) {
|
||||
Object* unreached = *object;
|
||||
*object = unreached->next;
|
||||
vm->numObjects--;
|
||||
free(unreached);
|
||||
} else {
|
||||
(*object)->marked = 0;
|
||||
object = &(*object)->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc(VM* vm) {
|
||||
int numObjects = vm->numObjects;
|
||||
markAll(vm);
|
||||
sweep(vm);
|
||||
vm->maxObjects = vm->numObjects * 2;
|
||||
printf("Collected %d objects, %d remaining.\n", numObjects - vm->numObjects,
|
||||
vm->numObjects);
|
||||
}
|
||||
|
||||
Object *newObject(VM* vm, ObjectType type) {
|
||||
if (vm->numObjects == vm->maxObjects) {
|
||||
gc(vm);
|
||||
}
|
||||
|
||||
Object* object = malloc(sizeof(Object));
|
||||
object->marked = 0;
|
||||
object->type = type;
|
||||
object->next = vm->root;
|
||||
vm->root = object;
|
||||
vm->numObjects++;
|
||||
return object;
|
||||
}
|
||||
|
||||
Object* pushInt(VM* vm, int value) {
|
||||
Object* object = newObject(vm, OBJ_INT);
|
||||
object->value = value;
|
||||
push(vm, object);
|
||||
return object;
|
||||
}
|
||||
|
||||
Object* pushPair(VM* vm) {
|
||||
Object* object = newObject(vm, OBJ_PAIR);
|
||||
object->head = pop(vm);
|
||||
object->tail = pop(vm);
|
||||
push(vm, object);
|
||||
return object;
|
||||
}
|
||||
|
||||
void freeVM(VM *vm) {
|
||||
vm->stackSize = 0;
|
||||
gc(vm);
|
||||
free(vm);
|
||||
}
|
||||
|
||||
void objectPrint(Object* object) {
|
||||
switch(object->type) {
|
||||
case OBJ_INT:
|
||||
printf("%d\n", object->value);
|
||||
break;
|
||||
|
||||
case OBJ_PAIR:
|
||||
printf("(");
|
||||
objectPrint(object->head);
|
||||
printf(", ");
|
||||
objectPrint(object->tail);
|
||||
printf(")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void test1() {
|
||||
printf("Test 1: Objects on stack are preserved.\n");
|
||||
VM* vm = newVM();
|
||||
pushInt(vm, 1);
|
||||
pushInt(vm, 2);
|
||||
|
||||
gc(vm);
|
||||
assert(vm->numObjects == 2, "Should have preserved objects.");
|
||||
freeVM(vm);
|
||||
}
|
||||
|
||||
void test2() {
|
||||
printf("Test 2: Unreached objects are collected.\n");
|
||||
VM* vm = newVM();
|
||||
pushInt(vm, 1);
|
||||
pushInt(vm, 2);
|
||||
pop(vm);
|
||||
pop(vm);
|
||||
|
||||
gc(vm);
|
||||
assert(vm->numObjects == 0, "Should have collected objects.");
|
||||
freeVM(vm);
|
||||
}
|
||||
|
||||
void test3() {
|
||||
printf("Test 3: Reach nested objects.\n");
|
||||
VM* vm = newVM();
|
||||
pushInt(vm, 1);
|
||||
pushInt(vm, 2);
|
||||
pushPair(vm);
|
||||
pushInt(vm, 3);
|
||||
pushInt(vm, 4);
|
||||
pushPair(vm);
|
||||
pushPair(vm);
|
||||
|
||||
gc(vm);
|
||||
assert(vm->numObjects == 7, "Should have reached objects.");
|
||||
freeVM(vm);
|
||||
}
|
||||
|
||||
void test4() {
|
||||
printf("Test 4: Handle cycles.\n");
|
||||
VM* vm = newVM();
|
||||
pushInt(vm, 1);
|
||||
pushInt(vm, 2);
|
||||
Object* a = pushPair(vm);
|
||||
pushInt(vm, 3);
|
||||
pushInt(vm, 4);
|
||||
Object* b = pushPair(vm);
|
||||
|
||||
/* Set up a cycle, and also make 2 and 4 unreachable and collectible. */
|
||||
a->tail = b;
|
||||
b->tail = a;
|
||||
|
||||
gc(vm);
|
||||
assert(vm->numObjects == 4, "Should have collected objects.");
|
||||
freeVM(vm);
|
||||
}
|
||||
|
||||
void perfTest() {
|
||||
printf("Performance Test.\n");
|
||||
VM* vm = newVM();
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
for (int j = 0; j < 20; j++) {
|
||||
pushInt(vm, i);
|
||||
}
|
||||
|
||||
for (int k = 0; k < 20; k++) {
|
||||
pop(vm);
|
||||
}
|
||||
}
|
||||
freeVM(vm);
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
perfTest();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue