#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "wren.h"
#include "wren_value.h"
#include "wren_vm.h"
#if WREN_DEBUG_TRACE_MEMORY
#include "wren_debug.h"
#endif
#define MIN_CAPACITY 16
#define GROW_FACTOR 2
#define MAP_LOAD_PERCENT 75
#define INITIAL_CALL_FRAMES 4
DEFINE_BUFFER(Value, Value);
DEFINE_BUFFER(Method, Method);
static void initObj(WrenVM* vm, Obj* obj, ObjType type, ObjClass* classObj)
{
obj->type = type;
obj->isDark = false;
obj->classObj = classObj;
obj->next = vm->first;
vm->first = obj;
}
ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name)
{
ObjClass* classObj = ALLOCATE(vm, ObjClass);
initObj(vm, &classObj->obj, OBJ_CLASS, NULL);
classObj->superclass = NULL;
classObj->numFields = numFields;
classObj->name = name;
classObj->attributes = NULL_VAL;
wrenPushRoot(vm, (Obj*)classObj);
wrenMethodBufferInit(&classObj->methods);
wrenPopRoot(vm);
return classObj;
}
void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass)
{
ASSERT(superclass != NULL, "Must have superclass.");
subclass->superclass = superclass;
if (subclass->numFields != -1)
{
subclass->numFields += superclass->numFields;
}
else
{
ASSERT(superclass->numFields == 0,
"A foreign class cannot inherit from a class with fields.");
}
for (int i = 0; i < superclass->methods.count; i++)
{
wrenBindMethod(vm, subclass, i, superclass->methods.data[i]);
}
}
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
ObjString* name)
{
Value metaclassName = wrenStringFormat(vm, "@ metaclass", OBJ_VAL(name));
wrenPushRoot(vm, AS_OBJ(metaclassName));
ObjClass* metaclass = wrenNewSingleClass(vm, 0, AS_STRING(metaclassName));
metaclass->obj.classObj = vm->classClass;
wrenPopRoot(vm);
wrenPushRoot(vm, (Obj*)metaclass);
wrenBindSuperclass(vm, metaclass, vm->classClass);
ObjClass* classObj = wrenNewSingleClass(vm, numFields, name);
wrenPushRoot(vm, (Obj*)classObj);
classObj->obj.classObj = metaclass;
wrenBindSuperclass(vm, classObj, superclass);
wrenPopRoot(vm);
wrenPopRoot(vm);
return classObj;
}
void wrenBindMethod(WrenVM* vm, ObjClass* classObj, int symbol, Method method)
{
if (symbol >= classObj->methods.count)
{
Method noMethod;
noMethod.type = METHOD_NONE;
wrenMethodBufferFill(vm, &classObj->methods, noMethod,
symbol - classObj->methods.count + 1);
}
classObj->methods.data[symbol] = method;
}
ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn)
{
ObjClosure* closure = ALLOCATE_FLEX(vm, ObjClosure,
ObjUpvalue*, fn->numUpvalues);
initObj(vm, &closure->obj, OBJ_CLOSURE, vm->fnClass);
closure->fn = fn;
for (int i = 0; i < fn->numUpvalues; i++) closure->upvalues[i] = NULL;
return closure;
}
ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure)
{
CallFrame* frames = ALLOCATE_ARRAY(vm, CallFrame, INITIAL_CALL_FRAMES);
int stackCapacity = closure == NULL
? 1
: wrenPowerOf2Ceil(closure->fn->maxSlots + 1);
Value* stack = ALLOCATE_ARRAY(vm, Value, stackCapacity);
ObjFiber* fiber = ALLOCATE(vm, ObjFiber);
initObj(vm, &fiber->obj, OBJ_FIBER, vm->fiberClass);
fiber->stack = stack;
fiber->stackTop = fiber->stack;
fiber->stackCapacity = stackCapacity;
fiber->frames = frames;
fiber->frameCapacity = INITIAL_CALL_FRAMES;
fiber->numFrames = 0;
fiber->openUpvalues = NULL;
fiber->caller = NULL;
fiber->error = NULL_VAL;
fiber->state = FIBER_OTHER;
if (closure != NULL)
{
wrenAppendCallFrame(vm, fiber, closure, fiber->stack);
fiber->stackTop[0] = OBJ_VAL(closure);
fiber->stackTop++;
}
return fiber;
}
void wrenEnsureStack(WrenVM* vm, ObjFiber* fiber, int needed)
{
if (fiber->stackCapacity >= needed) return;
int capacity = wrenPowerOf2Ceil(needed);
Value* oldStack = fiber->stack;
fiber->stack = (Value*)wrenReallocate(vm, fiber->stack,
sizeof(Value) * fiber->stackCapacity,
sizeof(Value) * capacity);
fiber->stackCapacity = capacity;
if (fiber->stack != oldStack)
{
if (vm->apiStack >= oldStack && vm->apiStack <= fiber->stackTop)
{
vm->apiStack = fiber->stack + (vm->apiStack - oldStack);
}
for (int i = 0; i < fiber->numFrames; i++)
{
CallFrame* frame = &fiber->frames[i];
frame->stackStart = fiber->stack + (frame->stackStart - oldStack);
}
for (ObjUpvalue* upvalue = fiber->openUpvalues;
upvalue != NULL;
upvalue = upvalue->next)
{
upvalue->value = fiber->stack + (upvalue->value - oldStack);
}
fiber->stackTop = fiber->stack + (fiber->stackTop - oldStack);
}
}
ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size)
{
ObjForeign* object = ALLOCATE_FLEX(vm, ObjForeign, uint8_t, size);
initObj(vm, &object->obj, OBJ_FOREIGN, classObj);
memset(object->data, 0, size);
return object;
}
ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module, int maxSlots)
{
FnDebug* debug = ALLOCATE(vm, FnDebug);
debug->name = NULL;
wrenIntBufferInit(&debug->sourceLines);
ObjFn* fn = ALLOCATE(vm, ObjFn);
initObj(vm, &fn->obj, OBJ_FN, vm->fnClass);
wrenValueBufferInit(&fn->constants);
wrenByteBufferInit(&fn->code);
fn->module = module;
fn->maxSlots = maxSlots;
fn->numUpvalues = 0;
fn->arity = 0;
fn->debug = debug;
return fn;
}
void wrenFunctionBindName(WrenVM* vm, ObjFn* fn, const char* name, int length)
{
fn->debug->name = ALLOCATE_ARRAY(vm, char, length + 1);
memcpy(fn->debug->name, name, length);
fn->debug->name[length] = '\0';
}
Value wrenNewInstance(WrenVM* vm, ObjClass* classObj)
{
ObjInstance* instance = ALLOCATE_FLEX(vm, ObjInstance,
Value, classObj->numFields);
initObj(vm, &instance->obj, OBJ_INSTANCE, classObj);
for (int i = 0; i < classObj->numFields; i++)
{
instance->fields[i] = NULL_VAL;
}
return OBJ_VAL(instance);
}
ObjList* wrenNewList(WrenVM* vm, uint32_t numElements)
{
Value* elements = NULL;
if (numElements > 0)
{
elements = ALLOCATE_ARRAY(vm, Value, numElements);
}
ObjList* list = ALLOCATE(vm, ObjList);
initObj(vm, &list->obj, OBJ_LIST, vm->listClass);
list->elements.capacity = numElements;
list->elements.count = numElements;
list->elements.data = elements;
return list;
}
void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint32_t index)
{
if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
wrenValueBufferWrite(vm, &list->elements, NULL_VAL);
if (IS_OBJ(value)) wrenPopRoot(vm);
for (uint32_t i = list->elements.count - 1; i > index; i--)
{
list->elements.data[i] = list->elements.data[i - 1];
}
list->elements.data[index] = value;
}
int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value)
{
int count = list->elements.count;
for (int i = 0; i < count; i++)
{
Value item = list->elements.data[i];
if(wrenValuesEqual(item, value)) {
return i;
}
}
return -1;
}
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint32_t index)
{
Value removed = list->elements.data[index];
if (IS_OBJ(removed)) wrenPushRoot(vm, AS_OBJ(removed));
for (int i = index; i < list->elements.count - 1; i++)
{
list->elements.data[i] = list->elements.data[i + 1];
}
if (list->elements.capacity / GROW_FACTOR >= list->elements.count)
{
list->elements.data = (Value*)wrenReallocate(vm, list->elements.data,
sizeof(Value) * list->elements.capacity,
sizeof(Value) * (list->elements.capacity / GROW_FACTOR));
list->elements.capacity /= GROW_FACTOR;
}
if (IS_OBJ(removed)) wrenPopRoot(vm);
list->elements.count--;
return removed;
}
ObjMap* wrenNewMap(WrenVM* vm)
{
ObjMap* map = ALLOCATE(vm, ObjMap);
initObj(vm, &map->obj, OBJ_MAP, vm->mapClass);
map->capacity = 0;
map->count = 0;
map->entries = NULL;
return map;
}
static inline uint32_t hashBits(uint64_t hash)
{
hash = ~hash + (hash << 18); hash = hash ^ (hash >> 31);
hash = hash * 21; hash = hash ^ (hash >> 11);
hash = hash + (hash << 6);
hash = hash ^ (hash >> 22);
return (uint32_t)(hash & 0x3fffffff);
}
static inline uint32_t hashNumber(double num)
{
return hashBits(wrenDoubleToBits(num));
}
static uint32_t hashObject(Obj* object)
{
switch (object->type)
{
case OBJ_CLASS:
return hashObject((Obj*)((ObjClass*)object)->name);
case OBJ_FN:
{
ObjFn* fn = (ObjFn*)object;
return hashNumber(fn->arity) ^ hashNumber(fn->code.count);
}
case OBJ_RANGE:
{
ObjRange* range = (ObjRange*)object;
return hashNumber(range->from) ^ hashNumber(range->to);
}
case OBJ_STRING:
return ((ObjString*)object)->hash;
default:
ASSERT(false, "Only immutable objects can be hashed.");
return 0;
}
}
static uint32_t hashValue(Value value)
{
#if WREN_NAN_TAGGING
if (IS_OBJ(value)) return hashObject(AS_OBJ(value));
return hashBits(value);
#else
switch (value.type)
{
case VAL_FALSE: return 0;
case VAL_NULL: return 1;
case VAL_NUM: return hashNumber(AS_NUM(value));
case VAL_TRUE: return 2;
case VAL_OBJ: return hashObject(AS_OBJ(value));
default: UNREACHABLE();
}
return 0;
#endif
}
static bool findEntry(MapEntry* entries, uint32_t capacity, Value key,
MapEntry** result)
{
if (capacity == 0) return false;
uint32_t startIndex = hashValue(key) % capacity;
uint32_t index = startIndex;
MapEntry* tombstone = NULL;
do
{
MapEntry* entry = &entries[index];
if (IS_UNDEFINED(entry->key))
{
if (IS_FALSE(entry->value))
{
*result = tombstone != NULL ? tombstone : entry;
return false;
}
else
{
if (tombstone == NULL) tombstone = entry;
}
}
else if (wrenValuesEqual(entry->key, key))
{
*result = entry;
return true;
}
index = (index + 1) % capacity;
}
while (index != startIndex);
ASSERT(tombstone != NULL, "Map should have tombstones or empty entries.");
*result = tombstone;
return false;
}
static bool insertEntry(MapEntry* entries, uint32_t capacity,
Value key, Value value)
{
ASSERT(entries != NULL, "Should ensure capacity before inserting.");
MapEntry* entry;
if (findEntry(entries, capacity, key, &entry))
{
entry->value = value;
return false;
}
else
{
entry->key = key;
entry->value = value;
return true;
}
}
static void resizeMap(WrenVM* vm, ObjMap* map, uint32_t capacity)
{
MapEntry* entries = ALLOCATE_ARRAY(vm, MapEntry, capacity);
for (uint32_t i = 0; i < capacity; i++)
{
entries[i].key = UNDEFINED_VAL;
entries[i].value = FALSE_VAL;
}
if (map->capacity > 0)
{
for (uint32_t i = 0; i < map->capacity; i++)
{
MapEntry* entry = &map->entries[i];
if (IS_UNDEFINED(entry->key)) continue;
insertEntry(entries, capacity, entry->key, entry->value);
}
}
DEALLOCATE(vm, map->entries);
map->entries = entries;
map->capacity = capacity;
}
Value wrenMapGet(ObjMap* map, Value key)
{
MapEntry* entry;
if (findEntry(map->entries, map->capacity, key, &entry)) return entry->value;
return UNDEFINED_VAL;
}
void wrenMapSet(WrenVM* vm, ObjMap* map, Value key, Value value)
{
if (map->count + 1 > map->capacity * MAP_LOAD_PERCENT / 100)
{
uint32_t capacity = map->capacity * GROW_FACTOR;
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
resizeMap(vm, map, capacity);
}
if (insertEntry(map->entries, map->capacity, key, value))
{
map->count++;
}
}
void wrenMapClear(WrenVM* vm, ObjMap* map)
{
DEALLOCATE(vm, map->entries);
map->entries = NULL;
map->capacity = 0;
map->count = 0;
}
Value wrenMapRemoveKey(WrenVM* vm, ObjMap* map, Value key)
{
MapEntry* entry;
if (!findEntry(map->entries, map->capacity, key, &entry)) return NULL_VAL;
Value value = entry->value;
entry->key = UNDEFINED_VAL;
entry->value = TRUE_VAL;
if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
map->count--;
if (map->count == 0)
{
wrenMapClear(vm, map);
}
else if (map->capacity > MIN_CAPACITY &&
map->count < map->capacity / GROW_FACTOR * MAP_LOAD_PERCENT / 100)
{
uint32_t capacity = map->capacity / GROW_FACTOR;
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
resizeMap(vm, map, capacity);
}
if (IS_OBJ(value)) wrenPopRoot(vm);
return value;
}
ObjModule* wrenNewModule(WrenVM* vm, ObjString* name)
{
ObjModule* module = ALLOCATE(vm, ObjModule);
initObj(vm, (Obj*)module, OBJ_MODULE, NULL);
wrenPushRoot(vm, (Obj*)module);
wrenSymbolTableInit(&module->variableNames);
wrenValueBufferInit(&module->variables);
module->name = name;
wrenPopRoot(vm);
return module;
}
Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive)
{
ObjRange* range = ALLOCATE(vm, ObjRange);
initObj(vm, &range->obj, OBJ_RANGE, vm->rangeClass);
range->from = from;
range->to = to;
range->isInclusive = isInclusive;
return OBJ_VAL(range);
}
static ObjString* allocateString(WrenVM* vm, size_t length)
{
ObjString* string = ALLOCATE_FLEX(vm, ObjString, char, length + 1);
initObj(vm, &string->obj, OBJ_STRING, vm->stringClass);
string->length = (int)length;
string->value[length] = '\0';
return string;
}
static void hashString(ObjString* string)
{
uint32_t hash = 2166136261u;
for (uint32_t i = 0; i < string->length; i++)
{
hash ^= string->value[i];
hash *= 16777619;
}
string->hash = hash;
}
Value wrenNewString(WrenVM* vm, const char* text)
{
return wrenNewStringLength(vm, text, strlen(text));
}
Value wrenNewStringLength(WrenVM* vm, const char* text, size_t length)
{
ASSERT(length == 0 || text != NULL, "Unexpected NULL string.");
ObjString* string = allocateString(vm, length);
if (length > 0 && text != NULL) memcpy(string->value, text, length);
hashString(string);
return OBJ_VAL(string);
}
Value wrenNewStringFromRange(WrenVM* vm, ObjString* source, int start,
uint32_t count, int step)
{
uint8_t* from = (uint8_t*)source->value;
int length = 0;
for (uint32_t i = 0; i < count; i++)
{
length += wrenUtf8DecodeNumBytes(from[start + i * step]);
}
ObjString* result = allocateString(vm, length);
result->value[length] = '\0';
uint8_t* to = (uint8_t*)result->value;
for (uint32_t i = 0; i < count; i++)
{
int index = start + i * step;
int codePoint = wrenUtf8Decode(from + index, source->length - index);
if (codePoint != -1)
{
to += wrenUtf8Encode(codePoint, to);
}
}
hashString(result);
return OBJ_VAL(result);
}
Value wrenNumToString(WrenVM* vm, double value)
{
if (isnan(value)) return CONST_STRING(vm, "nan");
if (isinf(value))
{
if (value > 0.0)
{
return CONST_STRING(vm, "infinity");
}
else
{
return CONST_STRING(vm, "-infinity");
}
}
char buffer[24];
int length = sprintf(buffer, "%.14g", value);
return wrenNewStringLength(vm, buffer, length);
}
Value wrenStringFromCodePoint(WrenVM* vm, int value)
{
int length = wrenUtf8EncodeNumBytes(value);
ASSERT(length != 0, "Value out of range.");
ObjString* string = allocateString(vm, length);
wrenUtf8Encode(value, (uint8_t*)string->value);
hashString(string);
return OBJ_VAL(string);
}
Value wrenStringFromByte(WrenVM *vm, uint8_t value)
{
int length = 1;
ObjString* string = allocateString(vm, length);
string->value[0] = value;
hashString(string);
return OBJ_VAL(string);
}
Value wrenStringFormat(WrenVM* vm, const char* format, ...)
{
va_list argList;
va_start(argList, format);
size_t totalLength = 0;
for (const char* c = format; *c != '\0'; c++)
{
switch (*c)
{
case '$':
totalLength += strlen(va_arg(argList, const char*));
break;
case '@':
totalLength += AS_STRING(va_arg(argList, Value))->length;
break;
default:
totalLength++;
}
}
va_end(argList);
ObjString* result = allocateString(vm, totalLength);
va_start(argList, format);
char* start = result->value;
for (const char* c = format; *c != '\0'; c++)
{
switch (*c)
{
case '$':
{
const char* string = va_arg(argList, const char*);
size_t length = strlen(string);
memcpy(start, string, length);
start += length;
break;
}
case '@':
{
ObjString* string = AS_STRING(va_arg(argList, Value));
memcpy(start, string->value, string->length);
start += string->length;
break;
}
default:
*start++ = *c;
}
}
va_end(argList);
hashString(result);
return OBJ_VAL(result);
}
Value wrenStringCodePointAt(WrenVM* vm, ObjString* string, uint32_t index)
{
ASSERT(index < string->length, "Index out of bounds.");
int codePoint = wrenUtf8Decode((uint8_t*)string->value + index,
string->length - index);
if (codePoint == -1)
{
char bytes[2];
bytes[0] = string->value[index];
bytes[1] = '\0';
return wrenNewStringLength(vm, bytes, 1);
}
return wrenStringFromCodePoint(vm, codePoint);
}
uint32_t wrenStringFind(ObjString* haystack, ObjString* needle, uint32_t start)
{
if (needle->length == 0) return start;
if (start + needle->length > haystack->length) return UINT32_MAX;
if (start >= haystack->length) return UINT32_MAX;
uint32_t shift[UINT8_MAX];
uint32_t needleEnd = needle->length - 1;
for (uint32_t index = 0; index < UINT8_MAX; index++)
{
shift[index] = needle->length;
}
for (uint32_t index = 0; index < needleEnd; index++)
{
char c = needle->value[index];
shift[(uint8_t)c] = needleEnd - index;
}
char lastChar = needle->value[needleEnd];
uint32_t range = haystack->length - needle->length;
for (uint32_t index = start; index <= range; )
{
char c = haystack->value[index + needleEnd];
if (lastChar == c &&
memcmp(haystack->value + index, needle->value, needleEnd) == 0)
{
return index;
}
index += shift[(uint8_t)c];
}
return UINT32_MAX;
}
ObjUpvalue* wrenNewUpvalue(WrenVM* vm, Value* value)
{
ObjUpvalue* upvalue = ALLOCATE(vm, ObjUpvalue);
initObj(vm, &upvalue->obj, OBJ_UPVALUE, NULL);
upvalue->value = value;
upvalue->closed = NULL_VAL;
upvalue->next = NULL;
return upvalue;
}
void wrenGrayObj(WrenVM* vm, Obj* obj)
{
if (obj == NULL) return;
if (obj->isDark) return;
obj->isDark = true;
if (vm->grayCount >= vm->grayCapacity)
{
vm->grayCapacity = vm->grayCount * 2;
vm->gray = (Obj**)vm->config.reallocateFn(vm->gray,
vm->grayCapacity * sizeof(Obj*),
vm->config.userData);
}
vm->gray[vm->grayCount++] = obj;
}
void wrenGrayValue(WrenVM* vm, Value value)
{
if (!IS_OBJ(value)) return;
wrenGrayObj(vm, AS_OBJ(value));
}
void wrenGrayBuffer(WrenVM* vm, ValueBuffer* buffer)
{
for (int i = 0; i < buffer->count; i++)
{
wrenGrayValue(vm, buffer->data[i]);
}
}
static void blackenClass(WrenVM* vm, ObjClass* classObj)
{
wrenGrayObj(vm, (Obj*)classObj->obj.classObj);
wrenGrayObj(vm, (Obj*)classObj->superclass);
for (int i = 0; i < classObj->methods.count; i++)
{
if (classObj->methods.data[i].type == METHOD_BLOCK)
{
wrenGrayObj(vm, (Obj*)classObj->methods.data[i].as.closure);
}
}
wrenGrayObj(vm, (Obj*)classObj->name);
if(!IS_NULL(classObj->attributes)) wrenGrayObj(vm, AS_OBJ(classObj->attributes));
vm->bytesAllocated += sizeof(ObjClass);
vm->bytesAllocated += classObj->methods.capacity * sizeof(Method);
}
static void blackenClosure(WrenVM* vm, ObjClosure* closure)
{
wrenGrayObj(vm, (Obj*)closure->fn);
for (int i = 0; i < closure->fn->numUpvalues; i++)
{
wrenGrayObj(vm, (Obj*)closure->upvalues[i]);
}
vm->bytesAllocated += sizeof(ObjClosure);
vm->bytesAllocated += sizeof(ObjUpvalue*) * closure->fn->numUpvalues;
}
static void blackenFiber(WrenVM* vm, ObjFiber* fiber)
{
for (int i = 0; i < fiber->numFrames; i++)
{
wrenGrayObj(vm, (Obj*)fiber->frames[i].closure);
}
for (Value* slot = fiber->stack; slot < fiber->stackTop; slot++)
{
wrenGrayValue(vm, *slot);
}
ObjUpvalue* upvalue = fiber->openUpvalues;
while (upvalue != NULL)
{
wrenGrayObj(vm, (Obj*)upvalue);
upvalue = upvalue->next;
}
wrenGrayObj(vm, (Obj*)fiber->caller);
wrenGrayValue(vm, fiber->error);
vm->bytesAllocated += sizeof(ObjFiber);
vm->bytesAllocated += fiber->frameCapacity * sizeof(CallFrame);
vm->bytesAllocated += fiber->stackCapacity * sizeof(Value);
}
static void blackenFn(WrenVM* vm, ObjFn* fn)
{
wrenGrayBuffer(vm, &fn->constants);
vm->bytesAllocated += sizeof(ObjFn);
vm->bytesAllocated += sizeof(uint8_t) * fn->code.capacity;
vm->bytesAllocated += sizeof(Value) * fn->constants.capacity;
vm->bytesAllocated += sizeof(int) * fn->code.capacity;
}
static void blackenForeign(WrenVM* vm, ObjForeign* foreign)
{
}
static void blackenInstance(WrenVM* vm, ObjInstance* instance)
{
wrenGrayObj(vm, (Obj*)instance->obj.classObj);
for (int i = 0; i < instance->obj.classObj->numFields; i++)
{
wrenGrayValue(vm, instance->fields[i]);
}
vm->bytesAllocated += sizeof(ObjInstance);
vm->bytesAllocated += sizeof(Value) * instance->obj.classObj->numFields;
}
static void blackenList(WrenVM* vm, ObjList* list)
{
wrenGrayBuffer(vm, &list->elements);
vm->bytesAllocated += sizeof(ObjList);
vm->bytesAllocated += sizeof(Value) * list->elements.capacity;
}
static void blackenMap(WrenVM* vm, ObjMap* map)
{
for (uint32_t i = 0; i < map->capacity; i++)
{
MapEntry* entry = &map->entries[i];
if (IS_UNDEFINED(entry->key)) continue;
wrenGrayValue(vm, entry->key);
wrenGrayValue(vm, entry->value);
}
vm->bytesAllocated += sizeof(ObjMap);
vm->bytesAllocated += sizeof(MapEntry) * map->capacity;
}
static void blackenModule(WrenVM* vm, ObjModule* module)
{
for (int i = 0; i < module->variables.count; i++)
{
wrenGrayValue(vm, module->variables.data[i]);
}
wrenBlackenSymbolTable(vm, &module->variableNames);
wrenGrayObj(vm, (Obj*)module->name);
vm->bytesAllocated += sizeof(ObjModule);
}
static void blackenRange(WrenVM* vm, ObjRange* range)
{
vm->bytesAllocated += sizeof(ObjRange);
}
static void blackenString(WrenVM* vm, ObjString* string)
{
vm->bytesAllocated += sizeof(ObjString) + string->length + 1;
}
static void blackenUpvalue(WrenVM* vm, ObjUpvalue* upvalue)
{
wrenGrayValue(vm, upvalue->closed);
vm->bytesAllocated += sizeof(ObjUpvalue);
}
static void blackenObject(WrenVM* vm, Obj* obj)
{
#if WREN_DEBUG_TRACE_MEMORY
printf("mark ");
wrenDumpValue(OBJ_VAL(obj));
printf(" @ %p\n", obj);
#endif
switch (obj->type)
{
case OBJ_CLASS: blackenClass( vm, (ObjClass*) obj); break;
case OBJ_CLOSURE: blackenClosure( vm, (ObjClosure*) obj); break;
case OBJ_FIBER: blackenFiber( vm, (ObjFiber*) obj); break;
case OBJ_FN: blackenFn( vm, (ObjFn*) obj); break;
case OBJ_FOREIGN: blackenForeign( vm, (ObjForeign*) obj); break;
case OBJ_INSTANCE: blackenInstance(vm, (ObjInstance*)obj); break;
case OBJ_LIST: blackenList( vm, (ObjList*) obj); break;
case OBJ_MAP: blackenMap( vm, (ObjMap*) obj); break;
case OBJ_MODULE: blackenModule( vm, (ObjModule*) obj); break;
case OBJ_RANGE: blackenRange( vm, (ObjRange*) obj); break;
case OBJ_STRING: blackenString( vm, (ObjString*) obj); break;
case OBJ_UPVALUE: blackenUpvalue( vm, (ObjUpvalue*) obj); break;
}
}
void wrenBlackenObjects(WrenVM* vm)
{
while (vm->grayCount > 0)
{
Obj* obj = vm->gray[--vm->grayCount];
blackenObject(vm, obj);
}
}
void wrenFreeObj(WrenVM* vm, Obj* obj)
{
#if WREN_DEBUG_TRACE_MEMORY
printf("free ");
wrenDumpValue(OBJ_VAL(obj));
printf(" @ %p\n", obj);
#endif
switch (obj->type)
{
case OBJ_CLASS:
wrenMethodBufferClear(vm, &((ObjClass*)obj)->methods);
break;
case OBJ_FIBER:
{
ObjFiber* fiber = (ObjFiber*)obj;
DEALLOCATE(vm, fiber->frames);
DEALLOCATE(vm, fiber->stack);
break;
}
case OBJ_FN:
{
ObjFn* fn = (ObjFn*)obj;
wrenValueBufferClear(vm, &fn->constants);
wrenByteBufferClear(vm, &fn->code);
wrenIntBufferClear(vm, &fn->debug->sourceLines);
DEALLOCATE(vm, fn->debug->name);
DEALLOCATE(vm, fn->debug);
break;
}
case OBJ_FOREIGN:
wrenFinalizeForeign(vm, (ObjForeign*)obj);
break;
case OBJ_LIST:
wrenValueBufferClear(vm, &((ObjList*)obj)->elements);
break;
case OBJ_MAP:
DEALLOCATE(vm, ((ObjMap*)obj)->entries);
break;
case OBJ_MODULE:
wrenSymbolTableClear(vm, &((ObjModule*)obj)->variableNames);
wrenValueBufferClear(vm, &((ObjModule*)obj)->variables);
break;
case OBJ_CLOSURE:
case OBJ_INSTANCE:
case OBJ_RANGE:
case OBJ_STRING:
case OBJ_UPVALUE:
break;
}
DEALLOCATE(vm, obj);
}
ObjClass* wrenGetClass(WrenVM* vm, Value value)
{
return wrenGetClassInline(vm, value);
}
bool wrenValuesEqual(Value a, Value b)
{
if (wrenValuesSame(a, b)) return true;
if (!IS_OBJ(a) || !IS_OBJ(b)) return false;
Obj* aObj = AS_OBJ(a);
Obj* bObj = AS_OBJ(b);
if (aObj->type != bObj->type) return false;
switch (aObj->type)
{
case OBJ_RANGE:
{
ObjRange* aRange = (ObjRange*)aObj;
ObjRange* bRange = (ObjRange*)bObj;
return aRange->from == bRange->from &&
aRange->to == bRange->to &&
aRange->isInclusive == bRange->isInclusive;
}
case OBJ_STRING:
{
ObjString* aString = (ObjString*)aObj;
ObjString* bString = (ObjString*)bObj;
return aString->hash == bString->hash &&
wrenStringEqualsCString(aString, bString->value, bString->length);
}
default:
return false;
}
}