瀏覽代碼

Fixed all GC related issues. For the first time Gravity is able to run all unittest with macro GRAVITY_GC_STRESSTEST set to 1.

Marco Bambini 6 年之前
父節點
當前提交
8b5bc314c1
共有 6 個文件被更改,包括 352 次插入195 次删除
  1. 146 96
      src/runtime/gravity_core.c
  2. 66 40
      src/runtime/gravity_vm.c
  3. 2 2
      src/runtime/gravity_vm.h
  4. 31 12
      src/runtime/gravity_vmmacros.h
  5. 103 42
      src/shared/gravity_value.c
  6. 4 3
      src/shared/gravity_value.h

+ 146 - 96
src/runtime/gravity_core.c

@@ -174,7 +174,7 @@ static inline gravity_value_t convert_map2string (gravity_vm *vm, gravity_map_t
 
 
     // get keys list
     // get keys list
     uint32_t count = gravity_hash_count(map->hash);
     uint32_t count = gravity_hash_count(map->hash);
-    gravity_list_t *list = gravity_list_new(vm, count);
+    gravity_list_t *list = gravity_list_new(NULL, count);
     gravity_hash_iterate(map->hash, map_keys_array, (void *)list);
     gravity_hash_iterate(map->hash, map_keys_array, (void *)list);
 
 
     count = (uint32_t) marray_size(list->array);
     count = (uint32_t) marray_size(list->array);
@@ -182,18 +182,25 @@ static inline gravity_value_t convert_map2string (gravity_vm *vm, gravity_map_t
         gravity_value_t key = marray_get(list->array, i);
         gravity_value_t key = marray_get(list->array, i);
         gravity_value_t *v = gravity_hash_lookup(map->hash, key);
         gravity_value_t *v = gravity_hash_lookup(map->hash, key);
         gravity_value_t value = (v) ? *v : VALUE_FROM_NULL;
         gravity_value_t value = (v) ? *v : VALUE_FROM_NULL;
+        gravity_value_t value_converted = VALUE_FROM_NULL;
 
 
         gravity_string_t *svalue;
         gravity_string_t *svalue;
         gravity_string_t *skey;
         gravity_string_t *skey;
-
-        if (!VALUE_ISA_STRING(key)) key = convert_value2string(vm, key);
+        bool key_to_free = false;
+        bool value_to_free = false;
+        
+        if (!VALUE_ISA_STRING(key)) {
+            key = convert_value2string(NULL, key);
+            key_to_free = true;
+        }
         skey = (VALUE_ISA_STRING(key)) ? VALUE_AS_STRING(key) : NULL;
         skey = (VALUE_ISA_STRING(key)) ? VALUE_AS_STRING(key) : NULL;
 
 
         if (VALUE_ISA_MAP(value) && (VALUE_AS_MAP(value) == map)) {
         if (VALUE_ISA_MAP(value) && (VALUE_AS_MAP(value) == map)) {
             svalue = NULL;
             svalue = NULL;
         } else {
         } else {
-            gravity_value_t value2 = convert_value2string(vm, value);
-            svalue = VALUE_ISA_VALID(value2) ? VALUE_AS_STRING(value2) : NULL;
+            value_converted = convert_value2string(NULL, value);
+            value_to_free = true;
+            svalue = VALUE_ISA_VALID(value_converted) ? VALUE_AS_STRING(value_converted) : NULL;
         }
         }
 
 
         // KEY
         // KEY
@@ -227,12 +234,16 @@ static inline gravity_value_t convert_map2string (gravity_vm *vm, gravity_map_t
             memcpy(buffer+pos, ",", 1);
             memcpy(buffer+pos, ",", 1);
             pos += 1;
             pos += 1;
         }
         }
+        
+        if (key_to_free && skey) gravity_value_free(NULL, key);
+        if (value_to_free && svalue) gravity_value_free(NULL, value_converted);
     }
     }
 
 
     // Write latest ] character
     // Write latest ] character
     memcpy(buffer+pos, "]", 1);
     memcpy(buffer+pos, "]", 1);
     buffer[++pos] = 0;
     buffer[++pos] = 0;
 
 
+    gravity_list_free(NULL, list);
     gravity_value_t result = VALUE_FROM_STRING(vm, buffer, pos);
     gravity_value_t result = VALUE_FROM_STRING(vm, buffer, pos);
     mem_free(buffer);
     mem_free(buffer);
     return result;
     return result;
@@ -989,61 +1000,62 @@ static bool list_reverse (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
 }
 }
 
 
 static bool list_reversed (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_reversed (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-  if (nargs > 1) RETURN_ERROR("Incorrect number of arguments.");
-  gravity_value_t value = GET_VALUE(0);                            // self parameter
-  gravity_list_t *list = VALUE_AS_LIST(value);
+    if (nargs > 1) RETURN_ERROR("Incorrect number of arguments.");
+    
+    // self parameter
+    gravity_list_t *list = VALUE_AS_LIST(GET_VALUE(0));
     gravity_list_t *newlist = gravity_list_new(vm, (uint32_t)list->array.n);
     gravity_list_t *newlist = gravity_list_new(vm, (uint32_t)list->array.n);
-  uint32_t count = (uint32_t)marray_size(list->array);
-  gravity_int_t i = 0;
+    uint32_t count = (uint32_t)marray_size(list->array);
+    gravity_int_t i = 0;
 
 
-  while (i < count) {
-    marray_push(gravity_value_t, newlist->array, marray_get(list->array, count-i-1));
-    i++;
-  }
+    while (i < count) {
+        marray_push(gravity_value_t, newlist->array, marray_get(list->array, count-i-1));
+        ++i;
+    }
 
 
-  RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
+    RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
 }
 }
 
 
 static bool compare_values(gravity_vm *vm, gravity_value_t selfvalue, gravity_value_t val1, gravity_value_t val2, gravity_closure_t *predicate) {
 static bool compare_values(gravity_vm *vm, gravity_value_t selfvalue, gravity_value_t val1, gravity_value_t val2, gravity_closure_t *predicate) {
-  gravity_value_t params[2] = {val1, val2};
-  if (!gravity_vm_runclosure(vm, predicate, selfvalue, params, 2)) return false;
-  gravity_value_t result = gravity_vm_result(vm);
-
-  //the conversion will make sure that the comparison function only returns a
-  //truthy value that can be interpreted as the result of a comparison
-  //(i.e. only integer, bool, float, null, undefined, or string)
-  gravity_value_t truthy_value = convert_value2bool(vm, result);
-  return truthy_value.n;
+    gravity_value_t params[2] = {val1, val2};
+    if (!gravity_vm_runclosure(vm, predicate, selfvalue, params, 2)) return false;
+    gravity_value_t result = gravity_vm_result(vm);
+    
+    //the conversion will make sure that the comparison function only returns a
+    //truthy value that can be interpreted as the result of a comparison
+    //(i.e. only integer, bool, float, null, undefined, or string)
+    gravity_value_t truthy_value = convert_value2bool(vm, result);
+    return truthy_value.n;
 }
 }
 
 
 static uint32_t partition(gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate) {
 static uint32_t partition(gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate) {
-  gravity_value_t pivot = array[high];
-  int32_t i = low - 1;
-
-  for (int32_t j = low; j <= high - 1; j++) {
-    if (!compare_values(vm, selfvalue, array[j], pivot, predicate)) {
-      i++;
-      gravity_value_t temp = array[i]; //swap a[i], a[j]
-      array[i] = array[j];
-      array[j] = temp;
+    gravity_value_t pivot = array[high];
+    int32_t i = low - 1;
+    
+    for (int32_t j = low; j <= high - 1; j++) {
+        if (!compare_values(vm, selfvalue, array[j], pivot, predicate)) {
+            ++i;
+            gravity_value_t temp = array[i]; //swap a[i], a[j]
+            array[i] = array[j];
+            array[j] = temp;
+        }
     }
     }
-  }
-
-  gravity_value_t temp = array[i + 1];
-  array[i + 1] = array[high];
-  array[high] = temp;
-
-  return i + 1;
+    
+    gravity_value_t temp = array[i + 1];
+    array[i + 1] = array[high];
+    array[high] = temp;
+    
+    return i + 1;
 }
 }
 
 
 static void quicksort(gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate) {
 static void quicksort(gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate) {
     if (gravity_vm_isaborted(vm)) return;
     if (gravity_vm_isaborted(vm)) return;
     
     
-  if (low < high) {
-    int32_t pi = partition(vm, array, low, high, selfvalue, predicate);
-    quicksort(vm, array, low, pi - 1, selfvalue, predicate);
-    quicksort(vm, array, pi + 1, high, selfvalue, predicate);
-  }
+    if (low < high) {
+        int32_t pi = partition(vm, array, low, high, selfvalue, predicate);
+        quicksort(vm, array, low, pi - 1, selfvalue, predicate);
+        quicksort(vm, array, pi + 1, high, selfvalue, predicate);
+    }
 }
 }
 
 
 static bool list_sort (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_sort (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -1061,62 +1073,76 @@ static bool list_sort (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, ui
 }
 }
 
 
 static bool list_sorted (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_sorted (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-  if (nargs != 2) RETURN_ERROR("One argument is needed by the sort function.");
-  if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
-  gravity_value_t selfvalue = GET_VALUE(0);                            // self parameter
+    if (nargs != 2) RETURN_ERROR("One argument is needed by the sort function.");
+    if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
+    gravity_value_t selfvalue = GET_VALUE(0);   // self parameter
 
 
-  //the predicate is the comparison function, passed to list.sort()
-  gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
-  gravity_list_t *list = VALUE_AS_LIST(selfvalue);
+    // the predicate is the comparison function, passed to list.sort()
+    gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
+    gravity_list_t *list = VALUE_AS_LIST(selfvalue);
     size_t count = marray_size(list->array);
     size_t count = marray_size(list->array);
-    gravity_list_t *newlist = gravity_list_new(vm, (uint32_t)count);
-
-  //memcpy should be faster than pushing element by element
-  memcpy(newlist->array.p, list->array.p, sizeof(gravity_value_t)*count);
-  newlist->array.m = list->array.m;
-  newlist->array.n = list->array.n;
+    
+    // do not transfer newlist to GC because it could be freed during predicate closure execution
+    // (because newlist is not yet in any stack)
+    gravity_list_t *newlist = gravity_list_new(NULL, (uint32_t)count);
+
+    //memcpy should be faster than pushing element by element
+    memcpy(newlist->array.p, list->array.p, sizeof(gravity_value_t)*count);
+    newlist->array.m = list->array.m;
+    newlist->array.n = list->array.n;
     quicksort(vm, newlist->array.p, 0, (int32_t)count-1, selfvalue, predicate);
     quicksort(vm, newlist->array.p, 0, (int32_t)count-1, selfvalue, predicate);
 
 
-  RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
+    gravity_vm_transfer(vm, (gravity_object_t*) newlist);
+    RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
 }
 }
 
 
 static bool list_map (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_map (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-  if (nargs != 2) RETURN_ERROR("One argument is needed by the map function.");
-  if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
-  gravity_value_t selfvalue = GET_VALUE(0);    // self parameter
-  gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
-  gravity_list_t *list = VALUE_AS_LIST(selfvalue);
-  size_t count = marray_size(list->array);
-  gravity_list_t *newlist = gravity_list_new(vm, (uint32_t)count);
-  newlist->array.m = list->array.m;
-  newlist->array.n = list->array.n;
-  for (uint32_t i = 0; i < count; i++) {
-    gravity_value_t *value = &marray_get(list->array, i);
-    if (!gravity_vm_runclosure(vm, predicate, selfvalue, value, 1)) return false;
-    gravity_value_t result = gravity_vm_result(vm);
-    marray_set(newlist->array, i, result);
-  }
-  RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
+    if (nargs != 2) RETURN_ERROR("One argument is needed by the map function.");
+    if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
+    
+    gravity_value_t selfvalue = GET_VALUE(0);    // self parameter
+    gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
+    gravity_list_t *list = VALUE_AS_LIST(selfvalue);
+    size_t count = marray_size(list->array);
+    
+    // do not transfer newlist to GC because it could be freed during predicate closure execution
+    gravity_list_t *newlist = gravity_list_new(NULL, (uint32_t)count);
+    newlist->array.m = list->array.m;
+    newlist->array.n = list->array.n;
+    for (uint32_t i = 0; i < count; i++) {
+        gravity_value_t *value = &marray_get(list->array, i);
+        if (!gravity_vm_runclosure(vm, predicate, selfvalue, value, 1)) return false;
+        gravity_value_t result = gravity_vm_result(vm);
+        marray_set(newlist->array, i, result);
+    }
+    
+    gravity_vm_transfer(vm, (gravity_object_t*) newlist);
+    RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
 }
 }
 
 
 static bool list_filter(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_filter(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-  if (nargs != 2) RETURN_ERROR("One argument is needed by the filter function.");
-  if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
-  gravity_value_t selfvalue = GET_VALUE(0);    // self parameter
-  gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
-  gravity_list_t *list = VALUE_AS_LIST(selfvalue);
-  size_t count = marray_size(list->array);
-  gravity_list_t *newlist = gravity_list_new(vm, (uint32_t)count);
-  for (uint32_t i = 0; i < count; i++) {
-    gravity_value_t *value = &marray_get(list->array, i);
-    if (!gravity_vm_runclosure(vm, predicate, selfvalue, value, 1)) return false;
-    gravity_value_t result = gravity_vm_result(vm);
-    gravity_value_t truthy_value = convert_value2bool(vm, result);
-    if (truthy_value.n) {
-      marray_push(gravity_value_t, newlist->array, *value);
+    if (nargs != 2) RETURN_ERROR("One argument is needed by the filter function.");
+    if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
+    
+    gravity_value_t selfvalue = GET_VALUE(0);    // self parameter
+    gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
+    gravity_list_t *list = VALUE_AS_LIST(selfvalue);
+    size_t count = marray_size(list->array);
+    
+    // do not transfer newlist to GC because it could be freed during predicate closure execution
+    gravity_list_t *newlist = gravity_list_new(NULL, (uint32_t)count);
+    for (uint32_t i = 0; i < count; i++) {
+        gravity_value_t *value = &marray_get(list->array, i);
+        if (!gravity_vm_runclosure(vm, predicate, selfvalue, value, 1)) return false;
+        gravity_value_t result = gravity_vm_result(vm);
+        gravity_value_t truthy_value = convert_value2bool(vm, result);
+        if (truthy_value.n) {
+            marray_push(gravity_value_t, newlist->array, *value);
+        }
     }
     }
-  }
-  RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
+    
+    gravity_vm_transfer(vm, (gravity_object_t*) newlist);
+    RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
 }
 }
 
 
 static bool list_reduce(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_reduce(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -1197,13 +1223,15 @@ static bool list_join (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, ui
 }
 }
 
 
 static bool list_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-    if ((nargs != 2) || (!VALUE_ISA_INT(GET_VALUE(1)))) RETURN_ERROR("An Int value is expected as argument of List allocate.");
+    if ((nargs != 2) || (!VALUE_ISA_INT(GET_VALUE(1)))) RETURN_ERROR("An Int value is expected as argument of list_exec.");
 
 
     uint32_t n = (uint32_t)VALUE_AS_INT(GET_VALUE(1));
     uint32_t n = (uint32_t)VALUE_AS_INT(GET_VALUE(1));
     gravity_list_t *list = gravity_list_new(vm, n);
     gravity_list_t *list = gravity_list_new(vm, n);
     if (!list) RETURN_ERROR("Maximum List allocation size reached (%d).", MAX_ALLOCATION);
     if (!list) RETURN_ERROR("Maximum List allocation size reached (%d).", MAX_ALLOCATION);
 
 
-    for (uint32_t i=0; i<n; ++i) marray_push(gravity_value_t, list->array, VALUE_FROM_NULL);
+    for (uint32_t i=0; i<n; ++i) {
+        marray_push(gravity_value_t, list->array, VALUE_FROM_NULL);
+    }
 
 
     RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
 }
 }
@@ -1217,7 +1245,7 @@ static bool map_count (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, ui
 }
 }
 
 
 static bool map_keys (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool map_keys (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-    #pragma unused(vm, nargs)
+    #pragma unused(nargs)
     gravity_map_t *map = VALUE_AS_MAP(GET_VALUE(0));
     gravity_map_t *map = VALUE_AS_MAP(GET_VALUE(0));
     uint32_t count = gravity_hash_count(map->hash);
     uint32_t count = gravity_hash_count(map->hash);
 
 
@@ -1321,7 +1349,8 @@ static bool map_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
     register gravity_int_t i = 0;
     register gravity_int_t i = 0;
 
 
     // build keys array
     // build keys array
-    gravity_list_t *list = gravity_list_new(vm, (uint32_t)n);
+    // do not transfer newlist to GC because it could be freed during closure execution
+    gravity_list_t *list = gravity_list_new(NULL, (uint32_t)n);
     gravity_hash_iterate(map->hash, map_keys_array, (void *)list);
     gravity_hash_iterate(map->hash, map_keys_array, (void *)list);
 
 
     nanotime_t t1 = nanotime();
     nanotime_t t1 = nanotime();
@@ -1330,6 +1359,8 @@ static bool map_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
         ++i;
         ++i;
     }
     }
     nanotime_t t2 = nanotime();
     nanotime_t t2 = nanotime();
+    
+    gravity_vm_transfer(vm, (gravity_object_t*) list);
     RETURN_VALUE(VALUE_FROM_INT(t2-t1), rindex);
     RETURN_VALUE(VALUE_FROM_INT(t2-t1), rindex);
 }
 }
 
 
@@ -1455,6 +1486,7 @@ static bool class_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     gravity_class_t *c = (gravity_class_t *)GET_VALUE(0).p;
     gravity_class_t *c = (gravity_class_t *)GET_VALUE(0).p;
 
 
     // perform alloc (then check for init)
     // perform alloc (then check for init)
+    gravity_gc_setenabled(vm, false);
     gravity_instance_t *instance = gravity_instance_new(vm, c);
     gravity_instance_t *instance = gravity_instance_new(vm, c);
 
 
     // if is inner class then ivar 0 is reserved for a reference to its outer class
     // if is inner class then ivar 0 is reserved for a reference to its outer class
@@ -1467,7 +1499,10 @@ static bool class_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     args[0] = VALUE_FROM_OBJECT(instance);
     args[0] = VALUE_FROM_OBJECT(instance);
 
 
     // if constructor found in this class then executes it
     // if constructor found in this class then executes it
-    if (closure) RETURN_CLOSURE(VALUE_FROM_OBJECT(closure), rindex);
+    if (closure) {
+        gravity_gc_setenabled(vm, true);
+        RETURN_CLOSURE(VALUE_FROM_OBJECT(closure), rindex);
+    }
 
 
     // no closure found (means no constructor found in this class)
     // no closure found (means no constructor found in this class)
     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
@@ -1476,7 +1511,8 @@ static bool class_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
         if (nargs != 1) RETURN_ERROR("No init with %d parameters found in class %s", nargs-1, c->identifier);
         if (nargs != 1) RETURN_ERROR("No init with %d parameters found in class %s", nargs-1, c->identifier);
         delegate->bridge_initinstance(vm, c->xdata, args[0], instance, args, nargs);
         delegate->bridge_initinstance(vm, c->xdata, args[0], instance, args, nargs);
     }
     }
-
+    
+    gravity_gc_setenabled(vm, true);
     // in any case set destination register to newly allocated instance
     // in any case set destination register to newly allocated instance
     RETURN_VALUE(VALUE_FROM_OBJECT(instance), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(instance), rindex);
 }
 }
@@ -2348,6 +2384,16 @@ static bool string_split (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     gravity_string_t *substr = VALUE_AS_STRING(GET_VALUE(1));
     gravity_string_t *substr = VALUE_AS_STRING(GET_VALUE(1));
     const char *sep = substr->s;
     const char *sep = substr->s;
     uint32_t seplen = substr->len;
     uint32_t seplen = substr->len;
+    
+    // this is a quite complex situation
+    // list should not be trasferred to GC bacause it could be freed by VALUE_FROM_STRING
+    // and the same applied to each VALUE_FROM_STRING
+    // but in order to use NULL as vm parameter I should keep track of each individual allocation here
+    // and then transfer all of them to the VM at the end of the loop (in order to be able to have them
+    // freed by the GC)
+    // I think it is too much work, the easier solution is just to disable GC during execution loop
+    
+    gravity_gc_setenabled(vm, false);
 
 
     // initialize the list to have a size of 0
     // initialize the list to have a size of 0
     gravity_list_t *list = gravity_list_new(vm, 0);
     gravity_list_t *list = gravity_list_new(vm, 0);
@@ -2361,6 +2407,7 @@ static bool string_split (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
             marray_push(gravity_value_t, list->array, VALUE_FROM_STRING(vm, original, 1));
             marray_push(gravity_value_t, list->array, VALUE_FROM_STRING(vm, original, 1));
             original += 1;
             original += 1;
         }
         }
+        gravity_gc_setenabled(vm, true);
         RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
         RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
     }
     }
 
 
@@ -2379,6 +2426,7 @@ static bool string_split (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
         slen -= vlen + seplen;
         slen -= vlen + seplen;
     }
     }
     
     
+    gravity_gc_setenabled(vm, true);
     RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
 }
 }
 
 
@@ -2779,9 +2827,11 @@ static bool system_exit (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
 // MARK: - CORE -
 // MARK: - CORE -
 
 
 gravity_closure_t *computed_property_create (gravity_vm *vm, gravity_function_t *getter_func, gravity_function_t *setter_func) {
 gravity_closure_t *computed_property_create (gravity_vm *vm, gravity_function_t *getter_func, gravity_function_t *setter_func) {
+    gravity_gc_setenabled(vm, false);
     gravity_closure_t *getter_closure = (getter_func) ? gravity_closure_new(vm, getter_func) : NULL;
     gravity_closure_t *getter_closure = (getter_func) ? gravity_closure_new(vm, getter_func) : NULL;
     gravity_closure_t *setter_closure = (setter_func) ? gravity_closure_new(vm, setter_func) : NULL;
     gravity_closure_t *setter_closure = (setter_func) ? gravity_closure_new(vm, setter_func) : NULL;
     gravity_function_t *f = gravity_function_new_special(vm, NULL, GRAVITY_COMPUTED_INDEX, getter_closure, setter_closure);
     gravity_function_t *f = gravity_function_new_special(vm, NULL, GRAVITY_COMPUTED_INDEX, getter_closure, setter_closure);
+    gravity_gc_setenabled(vm, true);
     return gravity_closure_new(vm, f);
     return gravity_closure_new(vm, f);
 }
 }
 
 

+ 66 - 40
src/runtime/gravity_vm.c

@@ -67,7 +67,7 @@ struct gravity_vm {
     gravity_float_t     gcratio;                        // ratio used in automatic recomputation of the new gcthreshold value
     gravity_float_t     gcratio;                        // ratio used in automatic recomputation of the new gcthreshold value
     gravity_int_t       gccount;                        // number of objects into GC
     gravity_int_t       gccount;                        // number of objects into GC
     gravity_object_r    graylist;                       // array of collected objects while GC is in process (gray list)
     gravity_object_r    graylist;                       // array of collected objects while GC is in process (gray list)
-    gravity_object_r    gcsave;                         // array of temp objects that need to be saved from GC
+    gravity_object_r    gctemp;                         // array of temp objects that need to be saved from GC
 
 
     // internal stats fields
     // internal stats fields
     #if GRAVITY_VM_STATS
     #if GRAVITY_VM_STATS
@@ -282,7 +282,7 @@ static inline bool gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber,
     }
     }
 
 
     // adjust upvalues ptr offset
     // adjust upvalues ptr offset
-    gravity_upvalue_t* upvalue = fiber->upvalues;
+    gravity_upvalue_t *upvalue = fiber->upvalues;
     while (upvalue) {
     while (upvalue) {
         upvalue->value += offset;
         upvalue->value += offset;
         upvalue = upvalue->next;
         upvalue = upvalue->next;
@@ -325,6 +325,7 @@ static gravity_upvalue_t *gravity_capture_upvalue (gravity_vm *vm, gravity_fiber
 
 
     // returns newly created upvalue
     // returns newly created upvalue
     newvalue->next = upvalue;
     newvalue->next = upvalue;
+    
     return newvalue;
     return newvalue;
 }
 }
 
 
@@ -367,14 +368,14 @@ bool gravity_isopt_class (gravity_class_t *c) {
 static bool gravity_vm_exec (gravity_vm *vm) {
 static bool gravity_vm_exec (gravity_vm *vm) {
     DECLARE_DISPATCH_TABLE;
     DECLARE_DISPATCH_TABLE;
 
 
-    gravity_fiber_t                *fiber = vm->fiber;            // current fiber
-    gravity_delegate_t            *delegate = vm->delegate;    // current delegate
-    gravity_callframe_t            *frame;                        // current executing frame
-    gravity_function_t            *func;                        // current executing function
-    gravity_value_t                *stackstart;                // SP => stack pointer
-    register uint32_t            *ip;                        // IP => instruction pointer
-    register uint32_t            inst;                        // IR => instruction register
-    register opcode_t            op;                            // OP => opcode register
+    gravity_fiber_t         *fiber = vm->fiber;         // current fiber
+    gravity_delegate_t      *delegate = vm->delegate;   // current delegate
+    gravity_callframe_t     *frame;                     // current executing frame
+    gravity_function_t      *func;                      // current executing function
+    gravity_value_t         *stackstart;                // SP => stack pointer
+    register uint32_t       *ip;                        // IP => instruction pointer
+    register uint32_t       inst;                       // IR => instruction register
+    register opcode_t       op;                         // OP => opcode register
 
 
     // load current callframe
     // load current callframe
     LOAD_FRAME();
     LOAD_FRAME();
@@ -439,7 +440,10 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     case EXEC_TYPE_INTERNAL: {
                     case EXEC_TYPE_INTERNAL: {
                         // backup register r1 because it can be overwrite to return a closure
                         // backup register r1 because it can be overwrite to return a closure
                         gravity_value_t r1copy = STACK_GET(r1);
                         gravity_value_t r1copy = STACK_GET(r1);
-                        if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
+                        BEGIN_TRUST_USERCODE(vm);
+                        bool result = closure->f->internal(vm, &stackstart[rwin], 2, r1);
+                        END_TRUST_USERCODE(vm);
+                        if (!result) {
                             if (vm->aborted) return false;
                             if (vm->aborted) return false;
 
 
                             // check for special getter trick
                             // check for special getter trick
@@ -458,7 +462,10 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 
 
                     case EXEC_TYPE_BRIDGED: {
                     case EXEC_TYPE_BRIDGED: {
                         DEBUG_ASSERT(delegate->bridge_getvalue, "bridge_getvalue delegate callback is mandatory");
                         DEBUG_ASSERT(delegate->bridge_getvalue, "bridge_getvalue delegate callback is mandatory");
-                        if (!delegate->bridge_getvalue(vm, closure->f->xdata, v2, VALUE_AS_CSTRING(v3), r1)) {
+                        BEGIN_TRUST_USERCODE(vm);
+                        bool result = delegate->bridge_getvalue(vm, closure->f->xdata, v2, VALUE_AS_CSTRING(v3), r1);
+                        END_TRUST_USERCODE(vm);
+                        if (!result) {
                             if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
                             if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
                         }
                         }
                     } break;
                     } break;
@@ -470,7 +477,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     } break;
                     } break;
                 }
                 }
                 LOAD_FRAME();
                 LOAD_FRAME();
-                SYNC_STACKTOP(current_fiber, MAXNUM(_rneed, rwin));
+                SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin));
 
 
                 // continue execution
                 // continue execution
                 DISPATCH();
                 DISPATCH();
@@ -573,7 +580,10 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     case EXEC_TYPE_INTERNAL: {
                     case EXEC_TYPE_INTERNAL: {
                         // backup register r1 because it can be overwrite to return a closure
                         // backup register r1 because it can be overwrite to return a closure
                         gravity_value_t r1copy = STACK_GET(r1);
                         gravity_value_t r1copy = STACK_GET(r1);
-                        if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
+                        BEGIN_TRUST_USERCODE(vm);
+                        bool result = closure->f->internal(vm, &stackstart[rwin], 2, r1);
+                        END_TRUST_USERCODE(vm);
+                        if (!result) {
                             if (vm->aborted) return false;
                             if (vm->aborted) return false;
 
 
                             // check for special getter trick
                             // check for special getter trick
@@ -592,7 +602,10 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 
 
                     case EXEC_TYPE_BRIDGED: {
                     case EXEC_TYPE_BRIDGED: {
                         DEBUG_ASSERT(delegate->bridge_setvalue, "bridge_setvalue delegate callback is mandatory");
                         DEBUG_ASSERT(delegate->bridge_setvalue, "bridge_setvalue delegate callback is mandatory");
-                        if (!delegate->bridge_setvalue(vm, closure->f->xdata, v2, VALUE_AS_CSTRING(v3), v1)) {
+                        BEGIN_TRUST_USERCODE(vm);
+                        bool result = delegate->bridge_setvalue(vm, closure->f->xdata, v2, VALUE_AS_CSTRING(v3), v1);
+                        END_TRUST_USERCODE(vm);
+                        if (!result) {
                             if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
                             if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
                         }
                         }
                     } break;
                     } break;
@@ -604,7 +617,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     } break;
                     } break;
                 }
                 }
                 LOAD_FRAME();
                 LOAD_FRAME();
-                SYNC_STACKTOP(current_fiber, MAXNUM(_rneed, rwin));
+                SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin));
 
 
                 // continue execution
                 // continue execution
                 DISPATCH();
                 DISPATCH();
@@ -1159,7 +1172,10 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     case EXEC_TYPE_INTERNAL: {
                     case EXEC_TYPE_INTERNAL: {
                         // backup register r1 because it can be overwrite to return a closure
                         // backup register r1 because it can be overwrite to return a closure
                         gravity_value_t r1copy = STACK_GET(r1);
                         gravity_value_t r1copy = STACK_GET(r1);
-                        if (!closure->f->internal(vm, &stackstart[rwin], r3, r1)) {
+                        BEGIN_TRUST_USERCODE(vm);
+                        bool result = closure->f->internal(vm, &stackstart[rwin], r3, r1);
+                        END_TRUST_USERCODE(vm);
+                        if (!result) {
                             if (vm->aborted) return false;
                             if (vm->aborted) return false;
 
 
                             // check for special getter trick
                             // check for special getter trick
@@ -1180,6 +1196,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 
 
                     case EXEC_TYPE_BRIDGED: {
                     case EXEC_TYPE_BRIDGED: {
                         bool result;
                         bool result;
+                        BEGIN_TRUST_USERCODE(vm);
                         if (VALUE_ISA_CLASS(v)) {
                         if (VALUE_ISA_CLASS(v)) {
                             DEBUG_ASSERT(delegate->bridge_initinstance, "bridge_initinstance delegate callback is mandatory");
                             DEBUG_ASSERT(delegate->bridge_initinstance, "bridge_initinstance delegate callback is mandatory");
                             gravity_instance_t *instance = (gravity_instance_t *)VALUE_AS_OBJECT(stackstart[rwin]);
                             gravity_instance_t *instance = (gravity_instance_t *)VALUE_AS_OBJECT(stackstart[rwin]);
@@ -1190,6 +1207,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                             // starting from version 0.4.4 we pass context object to execute in order to give the opportunity to pass it as self parameter to closures
                             // starting from version 0.4.4 we pass context object to execute in order to give the opportunity to pass it as self parameter to closures
                             result = delegate->bridge_execute(vm, closure->f->xdata, STACK_GET(0), &stackstart[rwin], r3, r1);
                             result = delegate->bridge_execute(vm, closure->f->xdata, STACK_GET(0), &stackstart[rwin], r3, r1);
                         }
                         }
+                        END_TRUST_USERCODE(vm);
                         if (!result && fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
                         if (!result && fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
                     } break;
                     } break;
 
 
@@ -1199,7 +1217,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 }
                 }
                 
                 
                 LOAD_FRAME();
                 LOAD_FRAME();
-                SYNC_STACKTOP(current_fiber, MAXNUM(_rneed, rwin));
+                SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin));
 
 
                 DISPATCH();
                 DISPATCH();
             }
             }
@@ -1280,7 +1298,6 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 DEBUG_VM("LISTNEW %d %d", r1, n);
                 DEBUG_VM("LISTNEW %d %d", r1, n);
 
 
                 gravity_list_t *list = gravity_list_new(vm, n);
                 gravity_list_t *list = gravity_list_new(vm, n);
-                if (!list) RUNTIME_ERROR("Unable allocate a List");
                 SETVALUE(r1, VALUE_FROM_OBJECT(list));
                 SETVALUE(r1, VALUE_FROM_OBJECT(list));
                 DISPATCH();
                 DISPATCH();
             }
             }
@@ -1352,8 +1369,10 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 gravity_value_t v = gravity_function_cpool_get(func, index);
                 gravity_value_t v = gravity_function_cpool_get(func, index);
                 if (!VALUE_ISA_FUNCTION(v)) RUNTIME_ERROR("Unable to create a closure from a non function object.");
                 if (!VALUE_ISA_FUNCTION(v)) RUNTIME_ERROR("Unable to create a closure from a non function object.");
                 gravity_function_t *f = VALUE_AS_FUNCTION(v);
                 gravity_function_t *f = VALUE_AS_FUNCTION(v);
-
-                // create closure
+                
+                gravity_gc_setenabled(vm, false);
+                
+                // create closure (outside GC)
                 gravity_closure_t *closure = gravity_closure_new(vm, f);
                 gravity_closure_t *closure = gravity_closure_new(vm, f);
 
 
                 // save current context (if any)
                 // save current context (if any)
@@ -1370,7 +1389,9 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     if (op != MOVE) RUNTIME_ERROR("Wrong OPCODE in CLOSURE statement");
                     if (op != MOVE) RUNTIME_ERROR("Wrong OPCODE in CLOSURE statement");
                     closure->upvalue[i] = (p2) ? gravity_capture_upvalue (vm, fiber, &stackstart[p1]) : frame->closure->upvalue[p1];
                     closure->upvalue[i] = (p2) ? gravity_capture_upvalue (vm, fiber, &stackstart[p1]) : frame->closure->upvalue[p1];
                 }
                 }
-
+                
+                // re-start GC
+                gravity_gc_setenabled(vm, true);
                 SETVALUE(r1, VALUE_FROM_OBJECT(closure));
                 SETVALUE(r1, VALUE_FROM_OBJECT(closure));
                 DISPATCH();
                 DISPATCH();
             }
             }
@@ -1428,7 +1449,7 @@ gravity_vm *gravity_vm_new (gravity_delegate_t *delegate) {
     vm->memallocated = 0;
     vm->memallocated = 0;
     vm->maxmemblock = MAX_MEMORY_BLOCK;
     vm->maxmemblock = MAX_MEMORY_BLOCK;
     marray_init(vm->graylist);
     marray_init(vm->graylist);
-    marray_init(vm->gcsave);
+    marray_init(vm->gctemp);
 
 
     // init base and core
     // init base and core
     gravity_core_register(vm);
     gravity_core_register(vm);
@@ -1449,7 +1470,7 @@ void gravity_vm_free (gravity_vm *vm) {
     if (vm->context) gravity_cache_free();
     if (vm->context) gravity_cache_free();
     gravity_vm_cleanup(vm);
     gravity_vm_cleanup(vm);
     if (vm->context) gravity_hash_free(vm->context);
     if (vm->context) gravity_hash_free(vm->context);
-    marray_destroy(vm->gcsave);
+    marray_destroy(vm->gctemp);
     marray_destroy(vm->graylist);
     marray_destroy(vm->graylist);
     mem_free(vm);
     mem_free(vm);
 }
 }
@@ -1564,8 +1585,8 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
     if ((f->tag == EXEC_TYPE_NATIVE) && ((!f->bytecode) || (f->ninsts == 0))) return true;
     if ((f->tag == EXEC_TYPE_NATIVE) && ((!f->bytecode) || (f->ninsts == 0))) return true;
 
 
     // current execution fiber
     // current execution fiber
-    gravity_fiber_t        *fiber = vm->fiber;
-    gravity_value_t        *stackstart = NULL;
+    gravity_fiber_t     *fiber = vm->fiber;
+    gravity_value_t     *stackstart = NULL;
     uint32_t            rwin = 0;
     uint32_t            rwin = 0;
 
 
     DEBUG_STACK();
     DEBUG_STACK();
@@ -1642,12 +1663,17 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
             break;
             break;
 
 
         case EXEC_TYPE_INTERNAL:
         case EXEC_TYPE_INTERNAL:
+            BEGIN_TRUST_USERCODE(vm);
             result = f->internal(vm, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
             result = f->internal(vm, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
+            END_TRUST_USERCODE(vm);
             break;
             break;
 
 
         case EXEC_TYPE_BRIDGED:
         case EXEC_TYPE_BRIDGED:
-            if (vm->delegate->bridge_execute)
+            if (vm->delegate->bridge_execute) {
+                BEGIN_TRUST_USERCODE(vm);
                 result = vm->delegate->bridge_execute(vm, f->xdata, selfvalue, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
                 result = vm->delegate->bridge_execute(vm, f->xdata, selfvalue, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
+                END_TRUST_USERCODE(vm);
+            }
             break;
             break;
 
 
         case EXEC_TYPE_SPECIAL:
         case EXEC_TYPE_SPECIAL:
@@ -1725,15 +1751,15 @@ void gravity_vm_set_callbacks (gravity_vm *vm, vm_transfer_cb vm_transfer, vm_cl
     vm->cleanup = vm_cleanup;
     vm->cleanup = vm_cleanup;
 }
 }
 
 
-void gravity_vm_transfer (gravity_vm* vm, gravity_object_t *obj) {
+void gravity_vm_transfer (gravity_vm *vm, gravity_object_t *obj) {
     if (vm->transfer) vm->transfer(vm, obj);
     if (vm->transfer) vm->transfer(vm, obj);
 }
 }
 
 
-void gravity_vm_cleanup (gravity_vm* vm) {
+void gravity_vm_cleanup (gravity_vm *vm) {
     if (vm->cleanup) vm->cleanup(vm);
     if (vm->cleanup) vm->cleanup(vm);
 }
 }
 
 
-void gravity_vm_filter (gravity_vm* vm, vm_filter_cb cleanup_filter) {
+void gravity_vm_filter (gravity_vm *vm, vm_filter_cb cleanup_filter) {
     vm->filter = cleanup_filter;
     vm->filter = cleanup_filter;
 }
 }
 
 
@@ -2001,7 +2027,6 @@ void gravity_gray_object (gravity_vm *vm, gravity_object_t *obj) {
 
 
     // avoid recursion if object has already been visited
     // avoid recursion if object has already been visited
     if (obj->gc.isdark) return;
     if (obj->gc.isdark) return;
-
     DEBUG_GC("GRAY %s", gravity_object_debug(obj, false));
     DEBUG_GC("GRAY %s", gravity_object_debug(obj, false));
 
 
     // object has been reached
     // object has been reached
@@ -2142,9 +2167,9 @@ void gravity_gc_start (gravity_vm *vm) {
     // reset memory counter
     // reset memory counter
     vm->memallocated = 0;
     vm->memallocated = 0;
 
 
-    // mark GC saved temp object
-    for (uint32_t i=0; i<marray_size(vm->gcsave); ++i) {
-        gravity_object_t *obj = marray_get(vm->gcsave, i);
+    // mark GC saved temp objects
+    for (uint32_t i=0; i<marray_size(vm->gctemp); ++i) {
+        gravity_object_t *obj = marray_get(vm->gctemp, i);
         gravity_gray_object(vm, obj);
         gravity_gray_object(vm, obj);
     }
     }
 
 
@@ -2234,20 +2259,21 @@ static void gravity_gc_cleanup (gravity_vm *vm) {
     vm->gchead = NULL;
     vm->gchead = NULL;
 
 
     // free all temporary allocated objects
     // free all temporary allocated objects
-    while (marray_size(vm->gcsave)) {
-        gravity_object_t *tobj = marray_pop(vm->gcsave);
+    while (marray_size(vm->gctemp)) {
+        gravity_object_t *tobj = marray_pop(vm->gctemp);
         gravity_object_free(vm, tobj);
         gravity_object_free(vm, tobj);
     }
     }
 }
 }
 
 
 void gravity_gc_setenabled (gravity_vm *vm, bool enabled) {
 void gravity_gc_setenabled (gravity_vm *vm, bool enabled) {
+    if (!vm) return;
     (enabled) ? ++vm->gcenabled : --vm->gcenabled ;
     (enabled) ? ++vm->gcenabled : --vm->gcenabled ;
 }
 }
 
 
-void gravity_gc_push (gravity_vm *vm, gravity_object_t *obj) {
-    marray_push(gravity_object_t *, vm->gcsave, obj);
+void gravity_gc_temppush (gravity_vm *vm, gravity_object_t *obj) {
+    marray_push(gravity_object_t *, vm->gctemp, obj);
 }
 }
 
 
-void gravity_gc_pop (gravity_vm *vm) {
-    marray_pop(vm->gcsave);
+void gravity_gc_temppop (gravity_vm *vm) {
+    marray_pop(vm->gctemp);
 }
 }

+ 2 - 2
src/runtime/gravity_vm.h

@@ -56,8 +56,8 @@ GRAVITY_API void                gravity_gray_value (gravity_vm* vm, gravity_valu
 GRAVITY_API void                gravity_gray_object (gravity_vm* vm, gravity_object_t *obj);
 GRAVITY_API void                gravity_gray_object (gravity_vm* vm, gravity_object_t *obj);
 GRAVITY_API void                gravity_gc_start (gravity_vm* vm);
 GRAVITY_API void                gravity_gc_start (gravity_vm* vm);
 GRAVITY_API void                gravity_gc_setenabled (gravity_vm* vm, bool enabled);
 GRAVITY_API void                gravity_gc_setenabled (gravity_vm* vm, bool enabled);
-GRAVITY_API void                gravity_gc_push (gravity_vm *vm, gravity_object_t *obj);
-GRAVITY_API void                gravity_gc_pop (gravity_vm *vm);
+GRAVITY_API void                gravity_gc_temppush (gravity_vm *vm, gravity_object_t *obj);
+GRAVITY_API void                gravity_gc_temppop (gravity_vm *vm);
 GRAVITY_API void                gravity_gc_setvalues (gravity_vm *vm, gravity_int_t threshold, gravity_int_t minthreshold, gravity_float_t ratio);
 GRAVITY_API void                gravity_gc_setvalues (gravity_vm *vm, gravity_int_t threshold, gravity_int_t minthreshold, gravity_float_t ratio);
     
     
 GRAVITY_API void                gravity_vm_transfer (gravity_vm* vm, gravity_object_t *obj);
 GRAVITY_API void                gravity_vm_transfer (gravity_vm* vm, gravity_object_t *obj);

+ 31 - 12
src/runtime/gravity_vmmacros.h

@@ -34,12 +34,15 @@
 #define OPCODE_GET_FOUR8bit(op,r1,r2,r3,r4)             r1 = (op >> 24) & 0xFF; r2 = (op >> 16) & 0xFF; r3 = (op >> 8) & 0xFF; r4 = (op & 0xFF)
 #define OPCODE_GET_FOUR8bit(op,r1,r2,r3,r4)             r1 = (op >> 24) & 0xFF; r2 = (op >> 16) & 0xFF; r3 = (op >> 8) & 0xFF; r4 = (op & 0xFF)
 #define OPCODE_GET_THREE8bit_ONE2bit(op,r1,r2,r3,r4)    r1 = (op >> 18) & 0xFF; r2 = (op >> 10) & 0xFF; r3 = (op >> 2) & 0xFF; r4 = (op & 0x03)
 #define OPCODE_GET_THREE8bit_ONE2bit(op,r1,r2,r3,r4)    r1 = (op >> 18) & 0xFF; r2 = (op >> 10) & 0xFF; r3 = (op >> 2) & 0xFF; r4 = (op & 0x03)
 
 
-#define GRAVITY_VM_DEGUB                0
-#define GRAVITY_VM_STATS                0
-#define GRAVITY_GC_STATS                0
-#define GRAVITY_GC_STRESSTEST           0
-#define GRAVITY_GC_DEBUG                0
-#define GRAVITY_STACK_DEBUG             0
+#define GRAVITY_VM_DEGUB                0               // print each VM instruction
+#define GRAVITY_VM_STATS                0               // print VM related stats after each execution
+#define GRAVITY_GC_STATS                0               // print useful stats each time GC runs
+#define GRAVITY_GC_STRESSTEST           0               // basically force a GC run after each memory allocation
+#define GRAVITY_GC_DEBUG                0               // print objects transferred and grayed
+#define GRAVITY_STACK_DEBUG             0               // dump the stack at each CALL and in some other places
+#define GRAVITY_TRUST_USERCODE          0               // set at 1 at your own risk!
+                                                        // when 0 each time an internal or a bridge function is executed the GC is disabled
+                                                        // in this way user does not have to completely understand how GC works under the hood
 
 
 #if GRAVITY_STACK_DEBUG
 #if GRAVITY_STACK_DEBUG
 #define DEBUG_STACK()                   gravity_stack_dump(fiber)
 #define DEBUG_STACK()                   gravity_stack_dump(fiber)
@@ -65,6 +68,14 @@
 #define INC_PC
 #define INC_PC
 #endif
 #endif
 
 
+#if GRAVITY_TRUST_USERCODE
+#define BEGIN_TRUST_USERCODE(_vm)
+#define END_TRUST_USERCODE(_vm)
+#else
+#define BEGIN_TRUST_USERCODE(_vm)       gravity_gc_setenabled(_vm, false)
+#define END_TRUST_USERCODE(_vm)         gravity_gc_setenabled(_vm, true)
+#endif
+
 #if GRAVITY_VM_STATS
 #if GRAVITY_VM_STATS
 #define RESET_STATS(_vm)                bzero(_vm->nstat, sizeof(_vm->nstat)); bzero(_vm->tstat, sizeof(_vm->tstat))
 #define RESET_STATS(_vm)                bzero(_vm->nstat, sizeof(_vm->nstat)); bzero(_vm->tstat, sizeof(_vm->tstat))
 #define PRINT_STATS(_vm)                gravity_vm_stats(_vm)
 #define PRINT_STATS(_vm)                gravity_vm_stats(_vm)
@@ -157,10 +168,12 @@
                                     cframe->outloop = false;                                                        \
                                     cframe->outloop = false;                                                        \
                                     cframe->args = (USE_ARGS(_c)) ? gravity_list_from_array(vm, _n-1, _s+1) : NULL
                                     cframe->args = (USE_ARGS(_c)) ? gravity_list_from_array(vm, _n-1, _s+1) : NULL
 
 
-#define SYNC_STACKTOP(_fiber,_n)    if (_fiber) _fiber->stacktop -= _n
-#define SETFRAME_OUTLOOP(cframe)    (cframe)->outloop = true
+// SYNC_STACKTOP has been modified in version 0.5.8 (December 4th 2018)
+// stack must be trashed ONLY in the fiber remains the same otherwise GC will collect stack values from a still active Fiber
+#define SYNC_STACKTOP(_fiber_saved, _fiber,_n)      if (_fiber_saved && (_fiber_saved == _fiber)) _fiber_saved->stacktop -= _n
+#define SETFRAME_OUTLOOP(cframe)                    (cframe)->outloop = true
 
 
-#define COMPUTE_JUMP(value)         (func->bytecode + (value))
+#define COMPUTE_JUMP(value)                         (func->bytecode + (value))
 
 
 // FAST MATH MACROS
 // FAST MATH MACROS
 #define FMATH_BIN_INT(_r1,_v2,_v3,_OP)              do {SETVALUE(_r1, VALUE_FROM_INT(_v2 _OP _v3)); DISPATCH();} while(0)
 #define FMATH_BIN_INT(_r1,_v2,_v3,_OP)              do {SETVALUE(_r1, VALUE_FROM_INT(_v2 _OP _v3)); DISPATCH();} while(0)
@@ -247,7 +260,10 @@
                                                         PUSH_FRAME(_c, &stackstart[rwin], r1, nargs);                   \
                                                         PUSH_FRAME(_c, &stackstart[rwin], r1, nargs);                   \
                                                     } break;                                                            \
                                                     } break;                                                            \
                                                     case EXEC_TYPE_INTERNAL: {                                          \
                                                     case EXEC_TYPE_INTERNAL: {                                          \
-                                                        if (!_c->f->internal(vm, &stackstart[rwin], nargs, r1)) {       \
+                                                        BEGIN_TRUST_USERCODE(vm);                                       \
+                                                        bool result = _c->f->internal(vm, &stackstart[rwin], nargs, r1);\
+                                                        END_TRUST_USERCODE(vm);                                         \
+                                                        if (!result) {       \
                                                             if (vm->aborted) return false;                              \
                                                             if (vm->aborted) return false;                              \
                                                             if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {                     \
                                                             if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {                     \
                                                                 closure = VALUE_AS_CLOSURE(STACK_GET(r1));              \
                                                                 closure = VALUE_AS_CLOSURE(STACK_GET(r1));              \
@@ -261,7 +277,10 @@
                                                     } break;                                                            \
                                                     } break;                                                            \
                                                     case EXEC_TYPE_BRIDGED:    {                                        \
                                                     case EXEC_TYPE_BRIDGED:    {                                        \
                                                         DEBUG_ASSERT(delegate->bridge_execute, "bridge_execute delegate callback is mandatory");        \
                                                         DEBUG_ASSERT(delegate->bridge_execute, "bridge_execute delegate callback is mandatory");        \
-                                                        if (!delegate->bridge_execute(vm, _c->f->xdata, STACK_GET(0), &stackstart[rwin], nargs, r1)) {  \
+                                                        BEGIN_TRUST_USERCODE(vm);                                       \
+                                                        bool result = delegate->bridge_execute(vm, _c->f->xdata, STACK_GET(0), &stackstart[rwin], nargs, r1); \
+                                                        END_TRUST_USERCODE(vm);                                         \
+                                                        if (!result) {                                                  \
                                                             if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);        \
                                                             if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);        \
                                                         }                                                               \
                                                         }                                                               \
                                                     } break;                                                            \
                                                     } break;                                                            \
@@ -270,7 +289,7 @@
                                                         break;                                                          \
                                                         break;                                                          \
                                                     }                                                                   \
                                                     }                                                                   \
                                                     LOAD_FRAME();                                                       \
                                                     LOAD_FRAME();                                                       \
-                                                    SYNC_STACKTOP(current_fiber, MAXNUM(_rneed, rwin))
+                                                    SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin))
 
 
 // MACROS used in core and optionals
 // MACROS used in core and optionals
 #define SETMETA_INITED(c)                           gravity_class_get_meta(c)->is_inited = true
 #define SETMETA_INITED(c)                           gravity_class_get_meta(c)->is_inited = true

+ 103 - 42
src/shared/gravity_value.c

@@ -16,6 +16,11 @@
 #include "gravity_opcodes.h"
 #include "gravity_opcodes.h"
 #include "gravity_vmmacros.h"
 #include "gravity_vmmacros.h"
 
 
+                                                // mark object visited to avoid infinite loop
+#define SET_OBJECT_VISITED_FLAG(_obj, _flag)    (((gravity_object_t *)_obj)->gc.visited = _flag)
+
+// MARK: -
+
 static void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json);
 static void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json);
 static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json);
 static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json);
 
 
@@ -100,7 +105,7 @@ gravity_module_t *gravity_module_new (gravity_vm *vm, const char *identifier) {
     m->identifier = string_dup(identifier);
     m->identifier = string_dup(identifier);
     m->htable = gravity_hash_create(0, gravity_value_hash, gravity_value_equals, gravity_hash_keyvaluefree, (void*)vm);
     m->htable = gravity_hash_create(0, gravity_value_hash, gravity_value_equals, gravity_hash_keyvaluefree, (void*)vm);
 
 
-    gravity_vm_transfer(vm, (gravity_object_t*)m);
+    if (vm) gravity_vm_transfer(vm, (gravity_object_t*)m);
     return m;
     return m;
 }
 }
 
 
@@ -113,9 +118,14 @@ void gravity_module_free (gravity_vm *vm, gravity_module_t *m) {
 }
 }
 
 
 uint32_t gravity_module_size (gravity_vm *vm, gravity_module_t *m) {
 uint32_t gravity_module_size (gravity_vm *vm, gravity_module_t *m) {
+    SET_OBJECT_VISITED_FLAG(m, true);
+    
     uint32_t hash_size = 0;
     uint32_t hash_size = 0;
     gravity_hash_iterate2(m->htable, gravity_hash_internalsize, (void*)&hash_size, (void*)vm);
     gravity_hash_iterate2(m->htable, gravity_hash_internalsize, (void*)&hash_size, (void*)vm);
-    return (sizeof(gravity_module_t)) + string_size(m->identifier) + hash_size + gravity_hash_memsize(m->htable);
+    uint32_t module_size = (sizeof(gravity_module_t)) + string_size(m->identifier) + hash_size + gravity_hash_memsize(m->htable);
+    
+    SET_OBJECT_VISITED_FLAG(m, false);
+    return module_size;
 }
 }
 
 
 void gravity_module_blacken (gravity_vm *vm, gravity_module_t *m) {
 void gravity_module_blacken (gravity_vm *vm, gravity_module_t *m) {
@@ -439,6 +449,8 @@ inline gravity_closure_t *gravity_class_lookup_constructor (gravity_class_t *c,
 }
 }
 
 
 uint32_t gravity_class_size (gravity_vm *vm, gravity_class_t *c) {
 uint32_t gravity_class_size (gravity_vm *vm, gravity_class_t *c) {
+    SET_OBJECT_VISITED_FLAG(c, true);
+    
     uint32_t class_size = sizeof(gravity_class_t) + (c->nivars * sizeof(gravity_value_t)) + string_size(c->identifier);
     uint32_t class_size = sizeof(gravity_class_t) + (c->nivars * sizeof(gravity_value_t)) + string_size(c->identifier);
 
 
     uint32_t hash_size = 0;
     uint32_t hash_size = 0;
@@ -449,6 +461,7 @@ uint32_t gravity_class_size (gravity_vm *vm, gravity_class_t *c) {
     if (c->xdata && delegate->bridge_size)
     if (c->xdata && delegate->bridge_size)
         class_size += delegate->bridge_size(vm, c->xdata);
         class_size += delegate->bridge_size(vm, c->xdata);
 
 
+    SET_OBJECT_VISITED_FLAG(c, false);
     return class_size;
     return class_size;
 }
 }
 
 
@@ -485,11 +498,14 @@ gravity_function_t *gravity_function_new (gravity_vm *vm, const char *identifier
     f->nupvalues = 0;
     f->nupvalues = 0;
 
 
     // only available in EXEC_TYPE_NATIVE case
     // only available in EXEC_TYPE_NATIVE case
-    f->useargs = false;
-    f->bytecode = (uint32_t *)code;
-    marray_init(f->cpool);
-    marray_init(f->pvalue);
-    marray_init(f->pname);
+    // code is != NULL when EXEC_TYPE_NATIVE
+    if (code != NULL) {
+        f->useargs = false;
+        f->bytecode = (uint32_t *)code;
+        marray_init(f->cpool);
+        marray_init(f->pvalue);
+        marray_init(f->pname);
+    }
 
 
     if (vm) gravity_vm_transfer(vm, (gravity_object_t*)f);
     if (vm) gravity_vm_transfer(vm, (gravity_object_t*)f);
     return f;
     return f;
@@ -502,7 +518,7 @@ gravity_function_t *gravity_function_new_internal (gravity_vm *vm, const char *i
     return f;
     return f;
 }
 }
 
 
-gravity_function_t    *gravity_function_new_special (gravity_vm *vm, const char *identifier, uint16_t index, void *getter, void *setter) {
+gravity_function_t *gravity_function_new_special (gravity_vm *vm, const char *identifier, uint16_t index, void *getter, void *setter) {
     gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
     gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
     f->tag = EXEC_TYPE_SPECIAL;
     f->tag = EXEC_TYPE_SPECIAL;
     f->index = index;
     f->index = index;
@@ -511,7 +527,7 @@ gravity_function_t    *gravity_function_new_special (gravity_vm *vm, const char
     return f;
     return f;
 }
 }
 
 
-gravity_function_t    *gravity_function_new_bridged (gravity_vm *vm, const char *identifier, void *xdata) {
+gravity_function_t *gravity_function_new_bridged (gravity_vm *vm, const char *identifier, void *xdata) {
     gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
     gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
     f->tag = EXEC_TYPE_BRIDGED;
     f->tag = EXEC_TYPE_BRIDGED;
     f->xdata = xdata;
     f->xdata = xdata;
@@ -1123,6 +1139,8 @@ void gravity_function_free (gravity_vm *vm, gravity_function_t *f) {
 }
 }
 
 
 uint32_t gravity_function_size (gravity_vm *vm, gravity_function_t *f) {
 uint32_t gravity_function_size (gravity_vm *vm, gravity_function_t *f) {
+    SET_OBJECT_VISITED_FLAG(f, true);
+    
     uint32_t func_size = sizeof(gravity_function_t) + string_size(f->identifier);
     uint32_t func_size = sizeof(gravity_function_t) + string_size(f->identifier);
 
 
     if (f->tag == EXEC_TYPE_NATIVE) {
     if (f->tag == EXEC_TYPE_NATIVE) {
@@ -1142,6 +1160,7 @@ uint32_t gravity_function_size (gravity_vm *vm, gravity_function_t *f) {
             func_size += delegate->bridge_size(vm, f->xdata);
             func_size += delegate->bridge_size(vm, f->xdata);
     }
     }
 
 
+    SET_OBJECT_VISITED_FLAG(f, false);
     return func_size;
     return func_size;
 }
 }
 
 
@@ -1166,8 +1185,6 @@ void gravity_function_blacken (gravity_vm *vm, gravity_function_t *f) {
 // MARK: -
 // MARK: -
 
 
 gravity_closure_t *gravity_closure_new (gravity_vm *vm, gravity_function_t *f) {
 gravity_closure_t *gravity_closure_new (gravity_vm *vm, gravity_function_t *f) {
-    #pragma unused(vm)
-
     gravity_closure_t *closure = (gravity_closure_t *)mem_alloc(NULL, sizeof(gravity_closure_t));
     gravity_closure_t *closure = (gravity_closure_t *)mem_alloc(NULL, sizeof(gravity_closure_t));
     assert(closure);
     assert(closure);
 
 
@@ -1193,13 +1210,16 @@ void gravity_closure_free (gravity_vm *vm, gravity_closure_t *closure) {
 
 
 uint32_t gravity_closure_size (gravity_vm *vm, gravity_closure_t *closure) {
 uint32_t gravity_closure_size (gravity_vm *vm, gravity_closure_t *closure) {
     #pragma unused(vm)
     #pragma unused(vm)
+    SET_OBJECT_VISITED_FLAG(closure, true);
 
 
     uint32_t closure_size = sizeof(gravity_closure_t);
     uint32_t closure_size = sizeof(gravity_closure_t);
     gravity_upvalue_t **upvalue = closure->upvalue;
     gravity_upvalue_t **upvalue = closure->upvalue;
-    while (upvalue) {
+    while (upvalue && upvalue[0]) {
         closure_size += sizeof(gravity_upvalue_t*);
         closure_size += sizeof(gravity_upvalue_t*);
         ++upvalue;
         ++upvalue;
     }
     }
+    
+    SET_OBJECT_VISITED_FLAG(closure, false);
     return closure_size;
     return closure_size;
 }
 }
 
 
@@ -1211,7 +1231,7 @@ void gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure) {
 
 
     // mark each upvalue
     // mark each upvalue
     gravity_upvalue_t **upvalue = closure->upvalue;
     gravity_upvalue_t **upvalue = closure->upvalue;
-    while (upvalue) {
+    while (upvalue && upvalue[0]) {
         gravity_gray_object(vm, (gravity_object_t*)upvalue[0]);
         gravity_gray_object(vm, (gravity_object_t*)upvalue[0]);
         ++upvalue;
         ++upvalue;
     }
     }
@@ -1223,7 +1243,6 @@ void gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure) {
 // MARK: -
 // MARK: -
 
 
 gravity_upvalue_t *gravity_upvalue_new (gravity_vm *vm, gravity_value_t *value) {
 gravity_upvalue_t *gravity_upvalue_new (gravity_vm *vm, gravity_value_t *value) {
-    #pragma unused(vm)
     gravity_upvalue_t *upvalue = (gravity_upvalue_t *)mem_alloc(NULL, sizeof(gravity_upvalue_t));
     gravity_upvalue_t *upvalue = (gravity_upvalue_t *)mem_alloc(NULL, sizeof(gravity_upvalue_t));
 
 
     upvalue->isa = gravity_class_upvalue;
     upvalue->isa = gravity_class_upvalue;
@@ -1231,27 +1250,34 @@ gravity_upvalue_t *gravity_upvalue_new (gravity_vm *vm, gravity_value_t *value)
     upvalue->closed = VALUE_FROM_NULL;
     upvalue->closed = VALUE_FROM_NULL;
     upvalue->next = NULL;
     upvalue->next = NULL;
 
 
-    if (vm) gravity_vm_transfer(vm, (gravity_object_t*)upvalue);
+    if (vm) gravity_vm_transfer(vm, (gravity_object_t*) upvalue);
     return upvalue;
     return upvalue;
 }
 }
 
 
+void gravity_upvalue_free(gravity_vm *vm, gravity_upvalue_t *upvalue) {
+    #pragma unused(vm)
+    
+    DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)upvalue, true));
+    mem_free(upvalue);
+}
+
 uint32_t gravity_upvalue_size (gravity_vm *vm, gravity_upvalue_t *upvalue) {
 uint32_t gravity_upvalue_size (gravity_vm *vm, gravity_upvalue_t *upvalue) {
     #pragma unused(vm, upvalue)
     #pragma unused(vm, upvalue)
-    return sizeof(gravity_upvalue_t);
+    
+    SET_OBJECT_VISITED_FLAG(upvalue, true);
+    uint32_t upvalue_size = sizeof(gravity_upvalue_t);
+    SET_OBJECT_VISITED_FLAG(upvalue, false);
+    
+    return upvalue_size;
 }
 }
 
 
 void gravity_upvalue_blacken (gravity_vm *vm, gravity_upvalue_t *upvalue) {
 void gravity_upvalue_blacken (gravity_vm *vm, gravity_upvalue_t *upvalue) {
     gravity_vm_memupdate(vm, gravity_upvalue_size(vm, upvalue));
     gravity_vm_memupdate(vm, gravity_upvalue_size(vm, upvalue));
+    // gray both closed and still opened values
+    gravity_gray_value(vm, *upvalue->value);
     gravity_gray_value(vm, upvalue->closed);
     gravity_gray_value(vm, upvalue->closed);
 }
 }
 
 
-void gravity_upvalue_free(gravity_vm *vm, gravity_upvalue_t *upvalue) {
-    #pragma unused(vm)
-
-    DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)upvalue, true));
-    mem_free(upvalue);
-}
-
 // MARK: -
 // MARK: -
 
 
 gravity_fiber_t *gravity_fiber_new (gravity_vm *vm, gravity_closure_t *closure, uint32_t nstack, uint32_t nframes) {
 gravity_fiber_t *gravity_fiber_new (gravity_vm *vm, gravity_closure_t *closure, uint32_t nstack, uint32_t nframes) {
@@ -1285,7 +1311,7 @@ gravity_fiber_t *gravity_fiber_new (gravity_vm *vm, gravity_closure_t *closure,
     // replace self with fiber instance
     // replace self with fiber instance
     frame->stackstart[0] = VALUE_FROM_OBJECT(fiber);
     frame->stackstart[0] = VALUE_FROM_OBJECT(fiber);
     
     
-    gravity_vm_transfer(vm, (gravity_object_t*) fiber);
+    if (vm) gravity_vm_transfer(vm, (gravity_object_t*) fiber);
     return fiber;
     return fiber;
 }
 }
 
 
@@ -1296,6 +1322,18 @@ void gravity_fiber_free (gravity_vm *vm, gravity_fiber_t *fiber) {
     if (fiber->error) mem_free(fiber->error);
     if (fiber->error) mem_free(fiber->error);
     mem_free(fiber->stack);
     mem_free(fiber->stack);
     mem_free(fiber->frames);
     mem_free(fiber->frames);
+    
+    // free upvalues
+    if (fiber->upvalues) {
+        gravity_upvalue_t *upvalue = fiber->upvalues;
+        gravity_upvalue_t *tempvalue;
+        while (upvalue) {
+            tempvalue = upvalue;
+            upvalue = upvalue->next;
+            gravity_upvalue_free(vm, tempvalue);
+        }
+    }
+    
     mem_free(fiber);
     mem_free(fiber);
 }
 }
 
 
@@ -1320,6 +1358,8 @@ void gravity_fiber_seterror (gravity_fiber_t *fiber, const char *error) {
 }
 }
 
 
 uint32_t gravity_fiber_size (gravity_vm *vm, gravity_fiber_t *fiber) {
 uint32_t gravity_fiber_size (gravity_vm *vm, gravity_fiber_t *fiber) {
+    SET_OBJECT_VISITED_FLAG(fiber, true);
+    
     // internal size
     // internal size
     uint32_t fiber_size = sizeof(gravity_fiber_t);
     uint32_t fiber_size = sizeof(gravity_fiber_t);
     fiber_size += fiber->stackalloc * sizeof(gravity_value_t);
     fiber_size += fiber->stackalloc * sizeof(gravity_value_t);
@@ -1333,24 +1373,25 @@ uint32_t gravity_fiber_size (gravity_vm *vm, gravity_fiber_t *fiber) {
     fiber_size += string_size(fiber->error);
     fiber_size += string_size(fiber->error);
     fiber_size += gravity_object_size(vm, (gravity_object_t *)fiber->caller);
     fiber_size += gravity_object_size(vm, (gravity_object_t *)fiber->caller);
 
 
+    SET_OBJECT_VISITED_FLAG(fiber, false);
     return fiber_size;
     return fiber_size;
 }
 }
 
 
 void gravity_fiber_blacken (gravity_vm *vm, gravity_fiber_t *fiber) {
 void gravity_fiber_blacken (gravity_vm *vm, gravity_fiber_t *fiber) {
     gravity_vm_memupdate(vm, gravity_fiber_size(vm, fiber));
     gravity_vm_memupdate(vm, gravity_fiber_size(vm, fiber));
-
+    
     // gray call frame functions
     // gray call frame functions
     for (uint32_t i=0; i < fiber->nframes; ++i) {
     for (uint32_t i=0; i < fiber->nframes; ++i) {
         gravity_gray_object(vm, (gravity_object_t *)fiber->frames[i].closure);
         gravity_gray_object(vm, (gravity_object_t *)fiber->frames[i].closure);
     }
     }
 
 
     // gray stack variables
     // gray stack variables
-    for (gravity_value_t* slot = fiber->stack; slot < fiber->stacktop; ++slot) {
+    for (gravity_value_t *slot = fiber->stack; slot < fiber->stacktop; ++slot) {
         gravity_gray_value(vm, *slot);
         gravity_gray_value(vm, *slot);
     }
     }
 
 
     // gray upvalues
     // gray upvalues
-    gravity_upvalue_t* upvalue = fiber->upvalues;
+    gravity_upvalue_t *upvalue = fiber->upvalues;
     while (upvalue) {
     while (upvalue) {
         gravity_gray_object(vm, (gravity_object_t *)upvalue);
         gravity_gray_object(vm, (gravity_object_t *)upvalue);
         upvalue = upvalue->next;
         upvalue = upvalue->next;
@@ -1499,7 +1540,10 @@ void gravity_object_free (gravity_vm *vm, gravity_object_t *obj) {
 
 
 uint32_t gravity_object_size (gravity_vm *vm, gravity_object_t *obj) {
 uint32_t gravity_object_size (gravity_vm *vm, gravity_object_t *obj) {
     if ((!obj) || (!OBJECT_IS_VALID(obj))) return 0;
     if ((!obj) || (!OBJECT_IS_VALID(obj))) return 0;
-
+    
+    // check if object has already been visited (to avoid infinite loop)
+    if (obj->gc.visited) return 0;
+    
     if (OBJECT_ISA_CLASS(obj)) return gravity_class_size(vm, (gravity_class_t *)obj);
     if (OBJECT_ISA_CLASS(obj)) return gravity_class_size(vm, (gravity_class_t *)obj);
     else if (OBJECT_ISA_FUNCTION(obj)) return gravity_function_size(vm, (gravity_function_t *)obj);
     else if (OBJECT_ISA_FUNCTION(obj)) return gravity_function_size(vm, (gravity_function_t *)obj);
     else if (OBJECT_ISA_CLOSURE(obj)) return gravity_closure_size(vm, (gravity_closure_t *)obj);
     else if (OBJECT_ISA_CLOSURE(obj)) return gravity_closure_size(vm, (gravity_closure_t *)obj);
@@ -1528,7 +1572,7 @@ void gravity_object_blacken (gravity_vm *vm, gravity_object_t *obj) {
     else if (OBJECT_ISA_MODULE(obj)) gravity_module_blacken(vm, (gravity_module_t *)obj);
     else if (OBJECT_ISA_MODULE(obj)) gravity_module_blacken(vm, (gravity_module_t *)obj);
     else if (OBJECT_ISA_STRING(obj)) gravity_string_blacken(vm, (gravity_string_t *)obj);
     else if (OBJECT_ISA_STRING(obj)) gravity_string_blacken(vm, (gravity_string_t *)obj);
     else if (OBJECT_ISA_UPVALUE(obj)) gravity_upvalue_blacken(vm, (gravity_upvalue_t *)obj);
     else if (OBJECT_ISA_UPVALUE(obj)) gravity_upvalue_blacken(vm, (gravity_upvalue_t *)obj);
-    else assert(0); // should never reach this point
+    //else assert(0); // should never reach this point
 }
 }
 
 
 // MARK: -
 // MARK: -
@@ -1600,12 +1644,15 @@ gravity_closure_t *gravity_instance_lookup_event (gravity_instance_t *i, const c
 }
 }
 
 
 uint32_t gravity_instance_size (gravity_vm *vm, gravity_instance_t *i) {
 uint32_t gravity_instance_size (gravity_vm *vm, gravity_instance_t *i) {
+    SET_OBJECT_VISITED_FLAG(i, true);
+    
     uint32_t instance_size = sizeof(gravity_instance_t) + (i->objclass->nivars * sizeof(gravity_value_t));
     uint32_t instance_size = sizeof(gravity_instance_t) + (i->objclass->nivars * sizeof(gravity_value_t));
 
 
     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
     if (i->xdata && delegate->bridge_size)
     if (i->xdata && delegate->bridge_size)
         instance_size += delegate->bridge_size(vm, i->xdata);
         instance_size += delegate->bridge_size(vm, i->xdata);
 
 
+    SET_OBJECT_VISITED_FLAG(i, false);
     return instance_size;
     return instance_size;
 }
 }
 
 
@@ -1721,11 +1768,7 @@ inline gravity_class_t *gravity_value_getsuper (gravity_value_t v) {
 }
 }
 
 
 void gravity_value_free (gravity_vm *vm, gravity_value_t v) {
 void gravity_value_free (gravity_vm *vm, gravity_value_t v) {
-    if (v.isa == gravity_class_int) return;
-    if (v.isa == gravity_class_float) return;
-    if (v.isa == gravity_class_bool) return;
-    if (v.isa == gravity_class_null) return;
-
+    if (!gravity_value_isobject(v)) return;
     gravity_object_free(vm, VALUE_AS_OBJECT(v));
     gravity_object_free(vm, VALUE_AS_OBJECT(v));
 }
 }
 
 
@@ -2009,13 +2052,12 @@ gravity_list_t *gravity_list_from_array (gravity_vm *vm, uint32_t n, gravity_val
     // and could be reused by other successive operations
     // and could be reused by other successive operations
     for (size_t i=0; i<n; ++i) marray_push(gravity_value_t, list->array, p[i]);
     for (size_t i=0; i<n; ++i) marray_push(gravity_value_t, list->array, p[i]);
 
 
-    gravity_vm_transfer(vm, (gravity_object_t*) list);
+    if (vm) gravity_vm_transfer(vm, (gravity_object_t*) list);
     return list;
     return list;
 }
 }
 
 
 void gravity_list_free (gravity_vm *vm, gravity_list_t *list) {
 void gravity_list_free (gravity_vm *vm, gravity_list_t *list) {
     #pragma unused(vm)
     #pragma unused(vm)
-
     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)list, true));
     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)list, true));
     marray_destroy(list->array);
     marray_destroy(list->array);
     mem_free((void *)list);
     mem_free((void *)list);
@@ -2031,12 +2073,17 @@ void gravity_list_append_list (gravity_vm *vm, gravity_list_t *list1, gravity_li
 }
 }
 
 
 uint32_t gravity_list_size (gravity_vm *vm, gravity_list_t *list) {
 uint32_t gravity_list_size (gravity_vm *vm, gravity_list_t *list) {
+    SET_OBJECT_VISITED_FLAG(list, true);
+    
     uint32_t internal_size = 0;
     uint32_t internal_size = 0;
     size_t count = marray_size(list->array);
     size_t count = marray_size(list->array);
     for (size_t i=0; i<count; ++i) {
     for (size_t i=0; i<count; ++i) {
         internal_size += gravity_value_size(vm, marray_get(list->array, i));
         internal_size += gravity_value_size(vm, marray_get(list->array, i));
     }
     }
-    return sizeof(gravity_list_t) + internal_size;
+    internal_size += sizeof(gravity_list_t);
+    
+    SET_OBJECT_VISITED_FLAG(list, false);
+    return internal_size;
 }
 }
 
 
 void gravity_list_blacken (gravity_vm *vm, gravity_list_t *list) {
 void gravity_list_blacken (gravity_vm *vm, gravity_list_t *list) {
@@ -2055,7 +2102,7 @@ gravity_map_t *gravity_map_new (gravity_vm *vm, uint32_t n) {
     map->isa = gravity_class_map;
     map->isa = gravity_class_map;
     map->hash = gravity_hash_create(n, gravity_value_hash, gravity_value_equals, NULL, NULL);
     map->hash = gravity_hash_create(n, gravity_value_hash, gravity_value_equals, NULL, NULL);
 
 
-    gravity_vm_transfer(vm, (gravity_object_t*) map);
+    if (vm) gravity_vm_transfer(vm, (gravity_object_t*) map);
     return map;
     return map;
 }
 }
 
 
@@ -2109,10 +2156,15 @@ abort_load:
 }
 }
 
 
 uint32_t gravity_map_size (gravity_vm *vm, gravity_map_t *map) {
 uint32_t gravity_map_size (gravity_vm *vm, gravity_map_t *map) {
+    SET_OBJECT_VISITED_FLAG(map, true);
+    
     uint32_t hash_size = 0;
     uint32_t hash_size = 0;
     gravity_hash_iterate2(map->hash, gravity_hash_internalsize, (void *)&hash_size, (void *)vm);
     gravity_hash_iterate2(map->hash, gravity_hash_internalsize, (void *)&hash_size, (void *)vm);
     hash_size += gravity_hash_memsize(map->hash);
     hash_size += gravity_hash_memsize(map->hash);
-    return sizeof(gravity_map_t) + hash_size;
+    hash_size += sizeof(gravity_map_t);
+    
+    SET_OBJECT_VISITED_FLAG(map, false);
+    return hash_size;
 }
 }
 
 
 void gravity_map_blacken (gravity_vm *vm, gravity_map_t *map) {
 void gravity_map_blacken (gravity_vm *vm, gravity_map_t *map) {
@@ -2129,7 +2181,7 @@ gravity_range_t *gravity_range_new (gravity_vm *vm, gravity_int_t from_range, gr
     range->from = from_range;
     range->from = from_range;
     range->to = (inclusive) ? to_range : --to_range;
     range->to = (inclusive) ? to_range : --to_range;
 
 
-    gravity_vm_transfer(vm, (gravity_object_t*) range);
+    if (vm) gravity_vm_transfer(vm, (gravity_object_t*) range);
     return range;
     return range;
 }
 }
 
 
@@ -2142,7 +2194,12 @@ void gravity_range_free (gravity_vm *vm, gravity_range_t *range) {
 
 
 uint32_t gravity_range_size (gravity_vm *vm, gravity_range_t *range) {
 uint32_t gravity_range_size (gravity_vm *vm, gravity_range_t *range) {
     #pragma unused(vm, range)
     #pragma unused(vm, range)
-    return sizeof(gravity_range_t);
+    
+    SET_OBJECT_VISITED_FLAG(range, true);
+    uint32_t range_size = sizeof(gravity_range_t);
+    SET_OBJECT_VISITED_FLAG(range, false);
+    
+    return range_size;
 }
 }
 
 
 void gravity_range_blacken (gravity_vm *vm, gravity_range_t *range) {
 void gravity_range_blacken (gravity_vm *vm, gravity_range_t *range) {
@@ -2202,7 +2259,11 @@ inline void gravity_string_free (gravity_vm *vm, gravity_string_t *value) {
 
 
 uint32_t gravity_string_size (gravity_vm *vm, gravity_string_t *string) {
 uint32_t gravity_string_size (gravity_vm *vm, gravity_string_t *string) {
     #pragma unused(vm)
     #pragma unused(vm)
-    return (sizeof(gravity_string_t)) + string->alloc;
+    SET_OBJECT_VISITED_FLAG(string, true);
+    uint32_t string_size = (sizeof(gravity_string_t)) + string->alloc;
+    SET_OBJECT_VISITED_FLAG(string, false);
+    
+    return string_size;
 }
 }
 
 
 void gravity_string_blacken (gravity_vm *vm, gravity_string_t *string) {
 void gravity_string_blacken (gravity_vm *vm, gravity_string_t *string) {

+ 4 - 3
src/shared/gravity_value.h

@@ -66,8 +66,8 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
-#define GRAVITY_VERSION						"0.5.6"     // git tag 0.5.6
-#define GRAVITY_VERSION_NUMBER				0x000506    // git push --tags
+#define GRAVITY_VERSION						"0.5.8"     // git tag 0.5.8
+#define GRAVITY_VERSION_NUMBER				0x000508    // git push --tags
 #define GRAVITY_BUILD_DATE                  __DATE__
 #define GRAVITY_BUILD_DATE                  __DATE__
 
 
 #ifndef GRAVITY_ENABLE_DOUBLE
 #ifndef GRAVITY_ENABLE_DOUBLE
@@ -242,6 +242,7 @@ typedef enum {
 
 
 typedef struct {
 typedef struct {
     bool                    isdark;         // flag to check if object is reachable
     bool                    isdark;         // flag to check if object is reachable
+    bool                    visited;        // flag to check if object has already been counted in memory size
     gravity_object_t        *next;          // to track next object in the linked list
     gravity_object_t        *next;          // to track next object in the linked list
 } gravity_gc_t;
 } gravity_gc_t;
 
 
@@ -445,7 +446,7 @@ GRAVITY_API uint32_t            gravity_closure_size (gravity_vm *vm, gravity_cl
 GRAVITY_API void                gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure);
 GRAVITY_API void                gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure);
 
 
 // MARK: - UPVALUE -
 // MARK: - UPVALUE -
-GRAVITY_API gravity_upvalue_t    *gravity_upvalue_new (gravity_vm *vm, gravity_value_t *value);
+GRAVITY_API gravity_upvalue_t   *gravity_upvalue_new (gravity_vm *vm, gravity_value_t *value);
 GRAVITY_API uint32_t            gravity_upvalue_size (gravity_vm *vm, gravity_upvalue_t *upvalue);
 GRAVITY_API uint32_t            gravity_upvalue_size (gravity_vm *vm, gravity_upvalue_t *upvalue);
 GRAVITY_API void                gravity_upvalue_blacken (gravity_vm *vm, gravity_upvalue_t *upvalue);
 GRAVITY_API void                gravity_upvalue_blacken (gravity_vm *vm, gravity_upvalue_t *upvalue);
 GRAVITY_API void                gravity_upvalue_free(gravity_vm *vm, gravity_upvalue_t *upvalue);
 GRAVITY_API void                gravity_upvalue_free(gravity_vm *vm, gravity_upvalue_t *upvalue);