memory.c
1 #include <kuroko/vm.h>
2 #include <kuroko/memory.h>
3 #include <kuroko/object.h>
4 #include <kuroko/compiler.h>
5 #include <kuroko/table.h>
6 #include <kuroko/util.h>
7 
8 #include "private.h"
9 
10 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
51 typedef struct DHE {
52  const void* ptr;
53  size_t size;
54  struct DHE * next;
55 } _dhe;
56 
61 #define DHE_SIZE 256
62 static struct DHE * _debug_mem[DHE_SIZE];
63 
64 static inline unsigned int _debug_mem_hash(const void * ptr) {
65  /* Pointers to objects are very often 16-byte aligned, and most
66  * allocations are of objects, so it would make sense to ignore
67  * the lower bits or we'll end up with a pretty bad hash. */
68  uintptr_t p = (uintptr_t)ptr;
69  return (p >> 4) & (DHE_SIZE-1);
70 }
71 
72 static void _debug_mem_set(const void* ptr, size_t size) {
73  unsigned int hash = _debug_mem_hash(ptr);
74  _dhe * x = _debug_mem[hash];
75  if (!x) {
76  x = malloc(sizeof(_dhe));
77  x->ptr = ptr;
78  x->size = size;
79  x->next = NULL;
80  _debug_mem[hash] = x;
81  } else {
82  _dhe * p = NULL;
83  do {
84  if (x->ptr == ptr) {
85  x->size = size;
86  return;
87  } else {
88  p = x;
89  x = x->next;
90  }
91  } while (x);
92  x = malloc(sizeof(_dhe));
93  x->ptr = ptr;
94  x->size = size;
95  x->next = NULL;
96  p->next = x;
97  }
98 }
99 
100 static size_t _debug_mem_get(const void * ptr) {
101  unsigned int hash = _debug_mem_hash(ptr);
102  _dhe * x = _debug_mem[hash];
103  if (!x) return 0;
104  do {
105  if (x->ptr == ptr) return x->size;
106  x = x->next;
107  } while (x);
108  return 0;
109 }
110 
111 static void _debug_mem_remove(const void *ptr) {
112  unsigned int hash = _debug_mem_hash(ptr);
113  _dhe * x = _debug_mem[hash];
114  if (!x) return;
115 
116  if (x->ptr == ptr) {
117  _debug_mem[hash] = x->next;
118  free(x);
119  } else {
120  _dhe * p = x;
121  x = x->next;
122  do {
123  if (x->ptr == ptr) {
124  p->next = x->next;
125  free(x);
126  return;
127  }
128  p = x;
129  x = x->next;
130  } while (x);
131  }
132 }
133 
134 static int _debug_mem_has(const void *ptr) {
135  unsigned int hash = _debug_mem_hash(ptr);
136  _dhe * x = _debug_mem[hash];
137  if (!x) return 0;
138  do {
139  if (x->ptr == ptr) return 1;
140  x = x->next;
141  } while (x);
142  return 0;
143 }
144 #endif
145 
146 void krk_gcTakeBytes(const void * ptr, size_t size) {
147 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
148  _debug_mem_set(ptr, size);
149 #endif
150 
151  vm.bytesAllocated += size;
152 }
153 
154 void * krk_reallocate(void * ptr, size_t old, size_t new) {
155 
156  vm.bytesAllocated -= old;
157  vm.bytesAllocated += new;
158 
159  if (new > old && ptr != krk_currentThread.stack && &krk_currentThread == vm.threads && !(vm.globalFlags & KRK_GLOBAL_GC_PAUSED)) {
160 #ifndef KRK_NO_STRESS_GC
161  if (vm.globalFlags & KRK_GLOBAL_ENABLE_STRESS_GC) {
163  }
164 #endif
165  if (vm.bytesAllocated > vm.nextGC) {
167  }
168  }
169 
170  void * out;
171  if (new == 0) {
172  free(ptr);
173  out = NULL;
174  } else {
175  out = realloc(ptr, new);
176  }
177 
178 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
179  if (ptr) {
180  if (!_debug_mem_has(ptr)) {
181  fprintf(stderr, "Invalid reallocation of %p from %zu to %zu\n", ptr, old, new);
182  abort();
183  }
184 
185  size_t t = _debug_mem_get(ptr);
186  if (t != old) {
187  fprintf(stderr, "Invalid reallocation of %p from %zu - should be %zu - to %zu\n", ptr, old, t, new);
188  abort();
189  }
190 
191  _debug_mem_remove(ptr);
192  }
193  if (out) {
194  _debug_mem_set(out, new);
195  }
196 #endif
197 
198  return out;
199 }
200 
201 static void freeObject(KrkObj * object) {
202  switch (object->type) {
203  case KRK_OBJ_STRING: {
204  KrkString * string = (KrkString*)object;
205  FREE_ARRAY(char, string->chars, string->length + 1);
206  if (string->codes && string->codes != string->chars) free(string->codes);
207  FREE(KrkString, object);
208  break;
209  }
210  case KRK_OBJ_CODEOBJECT: {
211  KrkCodeObject * function = (KrkCodeObject*)object;
212  krk_freeChunk(&function->chunk);
213  krk_freeValueArray(&function->positionalArgNames);
214  krk_freeValueArray(&function->keywordArgNames);
215  FREE_ARRAY(KrkLocalEntry, function->localNames, function->localNameCount);
216  function->localNameCount = 0;
217  FREE(KrkCodeObject, object);
218  break;
219  }
220  case KRK_OBJ_NATIVE: {
221  FREE(KrkNative, object);
222  break;
223  }
224  case KRK_OBJ_CLOSURE: {
225  KrkClosure * closure = (KrkClosure*)object;
226  FREE_ARRAY(KrkUpvalue*,closure->upvalues,closure->upvalueCount);
227  krk_freeTable(&closure->fields);
228  FREE(KrkClosure, object);
229  break;
230  }
231  case KRK_OBJ_UPVALUE: {
232  FREE(KrkUpvalue, object);
233  break;
234  }
235  case KRK_OBJ_CLASS: {
236  KrkClass * _class = (KrkClass*)object;
237  krk_freeTable(&_class->methods);
238  krk_freeTable(&_class->subclasses);
239  if (_class->base) {
240  krk_tableDeleteExact(&_class->base->subclasses, OBJECT_VAL(object));
241  }
242  FREE(KrkClass, object);
243  break;
244  }
245  case KRK_OBJ_INSTANCE: {
246  KrkInstance * inst = (KrkInstance*)object;
247  if (inst->_class->_ongcsweep) {
248  inst->_class->_ongcsweep(inst);
249  }
250  krk_freeTable(&inst->fields);
251  krk_reallocate(object,inst->_class->allocSize,0);
252  break;
253  }
254  case KRK_OBJ_BOUND_METHOD:
255  FREE(KrkBoundMethod, object);
256  break;
257  case KRK_OBJ_TUPLE: {
258  KrkTuple * tuple = (KrkTuple*)object;
259  krk_freeValueArray(&tuple->values);
260  FREE(KrkTuple, object);
261  break;
262  }
263  case KRK_OBJ_BYTES: {
264  KrkBytes * bytes = (KrkBytes*)object;
265  FREE_ARRAY(uint8_t, bytes->bytes, bytes->length);
266  FREE(KrkBytes, bytes);
267  break;
268  }
269  }
270 }
271 
272 void krk_freeObjects(void) {
273  KrkObj * object = vm.objects;
274  KrkObj * other = NULL;
275 
276  while (object) {
277  KrkObj * next = object->next;
278  if (object->type == KRK_OBJ_INSTANCE) {
279  freeObject(object);
280  } else {
281  object->next = other;
282  other = object;
283  }
284  object = next;
285  }
286 
287  while (other) {
288  KrkObj * next = other->next;
289  if (other->type == KRK_OBJ_CLASS) {
290  ((KrkClass*)other)->base = NULL;
291  }
292  freeObject(other);
293  other = next;
294  }
295 
296  free(vm.grayStack);
297 }
298 
299 void krk_freeMemoryDebugger(void) {
300 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
301  for (unsigned int i = 0; i < DHE_SIZE; ++i) {
302  _dhe * x = _debug_mem[i];
303  _debug_mem[i] = NULL;
304  while (x) {
305  _dhe * n = x->next;
306  free(x);
307  x = n;
308  }
309  }
310  if (vm.bytesAllocated != 0) {
311  abort();
312  }
313 #endif
314 }
315 
316 void krk_markObject(KrkObj * object) {
317  if (!object) return;
318  if (object->flags & KRK_OBJ_FLAGS_IS_MARKED) return;
319  object->flags |= KRK_OBJ_FLAGS_IS_MARKED;
320 
321  if (vm.grayCapacity < vm.grayCount + 1) {
322  vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
323  vm.grayStack = realloc(vm.grayStack, sizeof(KrkObj*) * vm.grayCapacity);
324  if (!vm.grayStack) exit(1);
325  }
326  vm.grayStack[vm.grayCount++] = object;
327 }
328 
329 void krk_markValue(KrkValue value) {
330  if (!IS_OBJECT(value)) return;
331  krk_markObject(AS_OBJECT(value));
332 }
333 
334 static void markArray(KrkValueArray * array) {
335  for (size_t i = 0; i < array->count; ++i) {
336  krk_markValue(array->values[i]);
337  }
338 }
339 
340 static void blackenObject(KrkObj * object) {
341  switch (object->type) {
342  case KRK_OBJ_CLOSURE: {
343  KrkClosure * closure = (KrkClosure *)object;
344  krk_markObject((KrkObj*)closure->function);
345  for (size_t i = 0; i < closure->upvalueCount; ++i) {
346  krk_markObject((KrkObj*)closure->upvalues[i]);
347  }
348  krk_markValue(closure->annotations);
349  krk_markTable(&closure->fields);
350  krk_markValue(closure->globalsOwner);
351  break;
352  }
353  case KRK_OBJ_CODEOBJECT: {
354  KrkCodeObject * function = (KrkCodeObject *)object;
355  krk_markObject((KrkObj*)function->name);
356  krk_markObject((KrkObj*)function->qualname);
357  krk_markObject((KrkObj*)function->docstring);
358  krk_markObject((KrkObj*)function->chunk.filename);
359  markArray(&function->positionalArgNames);
360  markArray(&function->keywordArgNames);
361  markArray(&function->chunk.constants);
362  for (size_t i = 0; i < function->localNameCount; ++i) {
363  krk_markObject((KrkObj*)function->localNames[i].name);
364  }
365  break;
366  }
367  case KRK_OBJ_UPVALUE:
368  krk_markValue(((KrkUpvalue*)object)->closed);
369  break;
370  case KRK_OBJ_CLASS: {
371  KrkClass * _class = (KrkClass *)object;
372  krk_markObject((KrkObj*)_class->name);
373  krk_markObject((KrkObj*)_class->filename);
374  krk_markObject((KrkObj*)_class->base);
375  krk_markObject((KrkObj*)_class->_class);
376  krk_markTable(&_class->methods);
377  break;
378  }
379  case KRK_OBJ_INSTANCE: {
380  krk_markObject((KrkObj*)((KrkInstance*)object)->_class);
381  if (((KrkInstance*)object)->_class->_ongcscan) ((KrkInstance*)object)->_class->_ongcscan((KrkInstance*)object);
382  krk_markTable(&((KrkInstance*)object)->fields);
383  break;
384  }
385  case KRK_OBJ_BOUND_METHOD: {
386  KrkBoundMethod * bound = (KrkBoundMethod *)object;
387  krk_markValue(bound->receiver);
388  krk_markObject((KrkObj*)bound->method);
389  break;
390  }
391  case KRK_OBJ_TUPLE: {
392  KrkTuple * tuple = (KrkTuple *)object;
393  markArray(&tuple->values);
394  break;
395  }
396  case KRK_OBJ_NATIVE:
397  case KRK_OBJ_STRING:
398  case KRK_OBJ_BYTES:
399  break;
400  }
401 }
402 
403 static void traceReferences(void) {
404  while (vm.grayCount > 0) {
405  KrkObj * object = vm.grayStack[--vm.grayCount];
406  blackenObject(object);
407  }
408 }
409 
410 static size_t sweep(void) {
411  KrkObj * previous = NULL;
412  KrkObj * object = vm.objects;
413  size_t count = 0;
414  while (object) {
415  if (object->flags & (KRK_OBJ_FLAGS_IMMORTAL | KRK_OBJ_FLAGS_IS_MARKED)) {
416  object->flags &= ~(KRK_OBJ_FLAGS_IS_MARKED | KRK_OBJ_FLAGS_SECOND_CHANCE);
417  previous = object;
418  object = object->next;
419  } else if (object->flags & KRK_OBJ_FLAGS_SECOND_CHANCE) {
420  KrkObj * unreached = object;
421  object = object->next;
422  if (previous != NULL) {
423  previous->next = object;
424  } else {
425  vm.objects = object;
426  }
427  freeObject(unreached);
428  count++;
429  } else {
430  object->flags |= KRK_OBJ_FLAGS_SECOND_CHANCE;
431  previous = object;
432  object = object->next;
433  }
434  }
435  return count;
436 }
437 
438 void krk_markTable(KrkTable * table) {
439  for (size_t i = 0; i < table->capacity; ++i) {
440  KrkTableEntry * entry = &table->entries[i];
441  krk_markValue(entry->key);
442  krk_markValue(entry->value);
443  }
444 }
445 
446 static void tableRemoveWhite(KrkTable * table) {
447  for (size_t i = 0; i < table->capacity; ++i) {
448  KrkTableEntry * entry = &table->entries[i];
449  if (IS_OBJECT(entry->key) && !((AS_OBJECT(entry->key))->flags & KRK_OBJ_FLAGS_IS_MARKED)) {
450  krk_tableDeleteExact(table, entry->key);
451  }
452  }
453 }
454 
455 static void markThreadRoots(KrkThreadState * thread) {
456  for (KrkValue * slot = thread->stack; slot && slot < thread->stackTop; ++slot) {
457  krk_markValue(*slot);
458  }
459  for (KrkUpvalue * upvalue = thread->openUpvalues; upvalue; upvalue = upvalue->next) {
460  krk_markObject((KrkObj*)upvalue);
461  }
463 
464  if (thread->module) krk_markObject((KrkObj*)thread->module);
465 
466  for (int i = 0; i < KRK_THREAD_SCRATCH_SIZE; ++i) {
467  krk_markValue(thread->scratchSpace[i]);
468  }
469 }
470 
471 static void markRoots(void) {
472  KrkThreadState * thread = vm.threads;
473  while (thread) {
474  markThreadRoots(thread);
475  thread = thread->next;
476  }
477 
478  krk_markObject((KrkObj*)vm.builtins);
479  krk_markTable(&vm.modules);
480 
481  if (vm.specialMethodNames) {
482  for (int i = 0; i < METHOD__MAX; ++i) {
483  krk_markValue(vm.specialMethodNames[i]);
484  }
485  }
486 }
487 
488 #ifndef KRK_NO_GC_TRACING
489 static int smartSize(char _out[100], size_t s) {
490 #if UINTPTR_MAX == 0xFFFFFFFF
491  size_t count = 3;
492  char * prefix = "GMK";
493 #else
494  size_t count = 5;
495  char * prefix = "PTGMK";
496 #endif
497  for (; count > 0 && *prefix; count--, prefix++) {
498  size_t base = 1UL << (count * 10);
499  if (s >= base) {
500  size_t t = s / base;
501  return snprintf(_out, 100, "%zu.%1zu %ciB", t, (s - t * base) / (base / 10), *prefix);
502  }
503  }
504  return snprintf(_out, 100, "%d B", (int)s);
505 }
506 #endif
507 
508 size_t krk_collectGarbage(void) {
509 #ifndef KRK_NO_GC_TRACING
510  struct timespec outTime, inTime;
511 
512  if (vm.globalFlags & KRK_GLOBAL_REPORT_GC_COLLECTS) {
513  clock_gettime(CLOCK_MONOTONIC, &inTime);
514  }
515 
516  size_t bytesBefore = vm.bytesAllocated;
517 #endif
518 
519  markRoots();
520  traceReferences();
521  tableRemoveWhite(&vm.strings);
522  size_t out = sweep();
523 
534  if (vm.bytesAllocated < 0x4000000) {
535  vm.nextGC = vm.bytesAllocated * 2;
536  } else {
537  vm.nextGC = vm.bytesAllocated + 0x4000000;
538  }
539 
540 #ifndef KRK_NO_GC_TRACING
541  if (vm.globalFlags & KRK_GLOBAL_REPORT_GC_COLLECTS) {
542  clock_gettime(CLOCK_MONOTONIC, &outTime);
543  struct timespec diff;
544  diff.tv_sec = outTime.tv_sec - inTime.tv_sec;
545  diff.tv_nsec = outTime.tv_nsec - inTime.tv_nsec;
546  if (diff.tv_nsec < 0) { diff.tv_sec--; diff.tv_nsec += 1000000000L; }
547 
548  char smartBefore[100];
549  smartSize(smartBefore, bytesBefore);
550  char smartAfter[100];
551  smartSize(smartAfter, vm.bytesAllocated);
552  char smartFreed[100];
553  smartSize(smartFreed, bytesBefore - vm.bytesAllocated);
554  char smartNext[100];
555  smartSize(smartNext, vm.nextGC);
556 
557  fprintf(stderr, "[gc] %lld.%.9lds %s before; %s after; freed %s in %llu objects; next collection at %s\n",
558  (long long)diff.tv_sec, diff.tv_nsec,
559  smartBefore,smartAfter,smartFreed,(unsigned long long)out, smartNext);
560  }
561 #endif
562  return out;
563 }
564 
565 #ifndef KRK_NO_SYSTEM_MODULES
566 KRK_Function(collect) {
567  FUNCTION_TAKES_NONE();
568  if (&krk_currentThread != vm.threads) return krk_runtimeError(vm.exceptions->valueError, "only the main thread can do that");
569  return INTEGER_VAL(krk_collectGarbage());
570 }
571 
572 KRK_Function(pause) {
573  FUNCTION_TAKES_NONE();
574  vm.globalFlags |= (KRK_GLOBAL_GC_PAUSED);
575  return NONE_VAL();
576 }
577 
578 KRK_Function(resume) {
579  FUNCTION_TAKES_NONE();
580  vm.globalFlags &= ~(KRK_GLOBAL_GC_PAUSED);
581  return NONE_VAL();
582 }
583 
584 void krk_module_init_gc(void) {
590  KrkInstance * gcModule = krk_newInstance(vm.baseClasses->moduleClass);
591  krk_attachNamedObject(&vm.modules, "gc", (KrkObj*)gcModule);
592  krk_attachNamedObject(&gcModule->fields, "__name__", (KrkObj*)S("gc"));
593  krk_attachNamedValue(&gcModule->fields, "__file__", NONE_VAL());
594  KRK_DOC(gcModule, "@brief Namespace containing methods for controlling the garbage collector.");
595 
596  KRK_DOC(BIND_FUNC(gcModule,collect),
597  "@brief Triggers one cycle of garbage collection.");
598  KRK_DOC(BIND_FUNC(gcModule,pause),
599  "@brief Disables automatic garbage collection until @ref resume is called.");
600  KRK_DOC(BIND_FUNC(gcModule,resume),
601  "@brief Re-enable automatic garbage collection after it was stopped by @ref pause ");
602 }
603 #endif
Exported methods for the source compiler.
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:445
Functions for dealing with garbage collection and memory allocation.
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
Definition: memory.c:329
void krk_markTable(KrkTable *table)
During a GC scan cycle, mark the contents of a table as used.
Definition: memory.c:438
void krk_freeObjects(void)
Release all objects.
Definition: memory.c:272
void krk_markObject(KrkObj *object)
During a GC scan cycle, mark an object as used.
Definition: memory.c:316
void * krk_reallocate(void *ptr, size_t old, size_t new)
Resize an allocated heap object.
Definition: memory.c:154
void krk_gcTakeBytes(const void *ptr, size_t size)
Assume ownership of size bytes at ptr.
Definition: memory.c:146
size_t krk_collectGarbage(void)
Run a cycle of the garbage collector.
Definition: memory.c:508
Struct definitions for core object types.
Internal header.
A function that has been attached to an object to serve as a method.
Definition: object.h:269
KrkValue receiver
Object to pass as implicit first argument.
Definition: object.h:271
KrkObj * method
Function to call.
Definition: object.h:272
Immutable sequence of bytes.
Definition: object.h:105
size_t length
Length of data in bytes.
Definition: object.h:107
uint8_t * bytes
Pointer to separately-stored bytes data.
Definition: object.h:108
void krk_freeChunk(KrkChunk *chunk)
Release the resources allocated to an opcode chunk.
Definition: chunk.c:42
Type object.
Definition: object.h:189
KrkString * filename
Filename of the original source that defined the codeobject for the class.
Definition: object.h:194
KrkCleanupCallback _ongcsweep
C function to call when the garbage collector is discarding an instance of this class.
Definition: object.h:198
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
Definition: object.h:197
KrkString * name
Name of the class.
Definition: object.h:193
struct KrkClass * base
Pointer to base class implementation.
Definition: object.h:195
KrkTable subclasses
Set of classes that subclass this class.
Definition: object.h:199
size_t allocSize
Size to allocate when creating instances of this class.
Definition: object.h:196
KrkTable methods
General attributes table.
Definition: object.h:192
struct KrkClass * _class
Metaclass.
Definition: object.h:191
Function object.
Definition: object.h:169
KrkCodeObject * function
The codeobject containing the bytecode run when this function is called.
Definition: object.h:171
KrkValue globalsOwner
Owner of the globals table for this function.
Definition: object.h:176
size_t upvalueCount
Number of entries in upvalues.
Definition: object.h:173
KrkValue annotations
Dictionary of type hints.
Definition: object.h:174
KrkUpvalue ** upvalues
Array of upvalues collected from the surrounding context when the closure was created.
Definition: object.h:172
KrkTable fields
Object attributes table.
Definition: object.h:175
Code object.
Definition: object.h:144
An object of a class.
Definition: object.h:255
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:339
KrkClass * _class
Type.
Definition: object.h:257
KrkTable fields
Attributes table.
Definition: object.h:258
Metadata on a local variable name in a function.
Definition: object.h:129
Managed binding to a C function.
Definition: object.h:283
The most basic object type.
Definition: object.h:41
uint16_t flags
General object flags, mostly related to garbage collection.
Definition: object.h:43
uint16_t type
Tag indicating core type.
Definition: object.h:42
struct KrkObj * next
Invasive linked list of all objects in the VM.
Definition: object.h:45
Immutable sequence of Unicode codepoints.
Definition: object.h:93
char * chars
UTF8 canonical data.
Definition: object.h:97
void * codes
Codepoint data.
Definition: object.h:98
size_t length
String length in bytes.
Definition: object.h:95
One (key,value) pair in a table.
Definition: table.h:20
Simple hash table of arbitrary keys to values.
Definition: table.h:28
int krk_tableDeleteExact(KrkTable *table, KrkValue key)
Remove a key from a hash table, with identity lookup.
Definition: table.c:220
void krk_attachNamedObject(KrkTable *table, const char name[], KrkObj *obj)
Attach an object to an attribute table.
Definition: vm.c:839
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:825
void krk_freeTable(KrkTable *table)
Release resources associated with a hash table.
Definition: table.c:20
Execution state of a VM thread.
Definition: vm.h:161
KrkValue currentException
Definition: vm.h:173
struct KrkThreadState * next
Definition: vm.h:162
KrkValue * stack
Definition: vm.h:167
KrkUpvalue * openUpvalues
Definition: vm.h:169
KrkValue scratchSpace[KRK_THREAD_SCRATCH_SIZE]
Definition: vm.h:177
KrkInstance * module
Definition: vm.h:172
Immutable sequence of arbitrary values.
Definition: object.h:297
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Definition: object.h:299
Storage for values referenced from nested functions.
Definition: object.h:115
struct KrkUpvalue * next
Invasive linked list pointer to next upvalue.
Definition: object.h:119
Flexible vector of stack references.
Definition: value.h:70
KrkValue * values
Definition: value.h:73
void krk_freeValueArray(KrkValueArray *array)
Release relesources used by a value array.
Definition: value.c:28
size_t count
Definition: value.h:72
Stack reference or primative value.
Implementation of a generic hash table.
Utilities for creating native bindings.
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:292
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:267
threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
#define KRK_THREAD_SCRATCH_SIZE
Extra space for each thread to store a set of working values safe from the GC.
Definition: vm.h:32
void krk_module_init_gc(void)
Initialize the built-in 'gc' module.
Definition: memory.c:584