Browse Source

Version 0.6.0

Much better stack usage during VM execution (much better memory management)
Fixed an issue with ivar access of superclass chain
Fixed an incorrect memory ivar allocation
Fixed an issue that occurs when maps are used as iterators
Added a missing header
Added three new unit test
All unittest passed even with the GRAVITY_GC_STRESSTEST macro set to 1
Removed unittest from gitignore
Marco Bambini 6 years ago
parent
commit
4a8c96d2ea

+ 6 - 6
src/compiler/gravity_semacheck2.c

@@ -123,12 +123,12 @@ static gnode_t *lookup_identifier (gvisitor_t *self, const char *identifier, gno
     char buffer[512];
 
     // get first node (the latest in the decls stack)
-    gnode_t    *base_node = gnode_array_get(decls, len-1);
+    gnode_t *base_node = gnode_array_get(decls, len-1);
     bool base_is_class = ISA(base_node, NODE_CLASS_DECL);
     bool base_is_static_function = (ISA(base_node, NODE_FUNCTION_DECL) && ((gnode_function_decl_t*)base_node)->storage == TOK_KEY_STATIC);
 
     for (int i=(int)len-1; i>=0; --i) {
-        gnode_t    *target = gnode_array_get(decls, i);
+        gnode_t *target = gnode_array_get(decls, i);
 
         // identify target type
         bool target_is_global = ISA(target, NODE_LIST_STAT);
@@ -150,7 +150,7 @@ static gnode_t *lookup_identifier (gvisitor_t *self, const char *identifier, gno
         }
         
         // lookup identifier is current target (obtained traversing the declaration stack)
-        gnode_t    *symbol = lookup_node(target, id);
+        gnode_t *symbol = lookup_node(target, id);
 
         // sanity check: if base_node is a class and symbol was found inside a func then report an error
         if (symbol && target_is_function && base_is_class) {
@@ -822,14 +822,14 @@ static void visit_variable_decl (gvisitor_t *self, gnode_variable_decl_t *node)
 
             // compute new ivar index
             (node->storage == TOK_KEY_STATIC) ? ++c->nsvar : ++c->nivar;
-            uint32_t n2 = 0;
 
             // super class is a static information so I can solve the fragile class problem at compilation time
             gnode_class_decl_t *super = (gnode_class_decl_t *)c->superclass;
             if (super && !NODE_ISA(super, NODE_CLASS_DECL)) return;
-
+            
+            uint32_t n2 = 0;
             while (super) {
-                n2 = (node->storage == TOK_KEY_STATIC) ? super->nsvar : super->nivar;
+                n2 += (node->storage == TOK_KEY_STATIC) ? super->nsvar : super->nivar;
                 super = (gnode_class_decl_t *)super->superclass;
             }
 

+ 26 - 14
src/runtime/gravity_core.c

@@ -432,17 +432,17 @@ inline gravity_value_t convert_value2string (gravity_vm *vm, gravity_value_t v)
 
     // sanity check (and break recursion)
     if ((!closure) || ((closure->f->tag == EXEC_TYPE_INTERNAL) && (closure->f->internal == convert_object_string)) || gravity_vm_getclosure(vm) == closure) {
-        if (VALUE_ISA_INSTANCE(v)) {
-            gravity_instance_t *instance = VALUE_AS_INSTANCE(v);
-            if (instance->xdata) {
-                gravity_delegate_t *delegate = gravity_vm_delegate(vm);
-                if (delegate->bridge_string) {
-                    uint32_t len = 0;
-                    const char *s = delegate->bridge_string(vm, instance->xdata, &len);
-                    if (s) return VALUE_FROM_STRING(vm, s, len);
-                }
-            } else {
-                char buffer[512];
+		if (VALUE_ISA_INSTANCE(v)) {
+			gravity_instance_t *instance = VALUE_AS_INSTANCE(v);
+			if (vm && instance->xdata) {
+				gravity_delegate_t *delegate = gravity_vm_delegate(vm);
+				if (delegate->bridge_string) {
+					uint32_t len = 0;
+					const char *s = delegate->bridge_string(vm, instance->xdata, &len);
+					if (s) return VALUE_FROM_STRING(vm, s, len);
+				}
+			} else {
+				char buffer[512];
                 const char *identifier = (instance->objclass->identifier);
                 if (!identifier) identifier = "anonymous class";
                 snprintf(buffer, sizeof(buffer), "instance of %s (%p)", identifier, instance);
@@ -1356,6 +1356,17 @@ static bool map_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
     RETURN_VALUE(VALUE_FROM_INT(t2-t1), rindex);
 }
 
+static bool map_iterator (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, args, nargs)
+    // fix for a bug encountered in the following code
+    // var r = ["k1": 123, "k2": 142];
+    // for (var data in r) {}
+    // the for loop will result in an infinite loop
+    // because the special ITERATOR_INIT_FUNCTION key
+    // will result in a NULL value (not FALSE)
+    RETURN_VALUE(VALUE_FROM_FALSE, rindex);
+}
+
 // MARK: - Range Class -
 
 static bool range_count (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -2993,6 +3004,7 @@ static void gravity_core_init (void) {
     gravity_class_bind(gravity_class_map, GRAVITY_INTERNAL_LOADAT_NAME, NEW_CLOSURE_VALUE(map_loadat));
     gravity_class_bind(gravity_class_map, GRAVITY_INTERNAL_STOREAT_NAME, NEW_CLOSURE_VALUE(map_storeat));
     gravity_class_bind(gravity_class_map, "hasKey", NEW_CLOSURE_VALUE(map_haskey));
+    gravity_class_bind(gravity_class_map, ITERATOR_INIT_FUNCTION, NEW_CLOSURE_VALUE(map_iterator));
     #if GRAVITY_MAP_DOTSUGAR
     gravity_class_bind(gravity_class_map, GRAVITY_INTERNAL_LOAD_NAME, NEW_CLOSURE_VALUE(map_load));
     gravity_class_bind(gravity_class_map, GRAVITY_INTERNAL_STORE_NAME, NEW_CLOSURE_VALUE(map_storeat));
@@ -3096,8 +3108,8 @@ static void gravity_core_init (void) {
     gravity_class_bind(gravity_class_string, "lower", NEW_CLOSURE_VALUE(string_lower));
     gravity_class_bind(gravity_class_string, "split", NEW_CLOSURE_VALUE(string_split));
     gravity_class_bind(gravity_class_string, "loop", NEW_CLOSURE_VALUE(string_loop));
-    gravity_class_bind(gravity_class_string, "iterate", NEW_CLOSURE_VALUE(string_iterator));
-    gravity_class_bind(gravity_class_string, "next", NEW_CLOSURE_VALUE(string_iterator_next));
+    gravity_class_bind(gravity_class_string, ITERATOR_INIT_FUNCTION, NEW_CLOSURE_VALUE(string_iterator));
+    gravity_class_bind(gravity_class_string, ITERATOR_NEXT_FUNCTION, NEW_CLOSURE_VALUE(string_iterator_next));
     gravity_class_bind(gravity_class_string, "number", NEW_CLOSURE_VALUE(string_number));
     // Meta
     gravity_class_t *string_meta = gravity_class_get_meta(gravity_class_string);
@@ -3133,7 +3145,7 @@ static void gravity_core_init (void) {
     gravity_class_bind(gravity_class_null, GRAVITY_INTERNAL_STORE_NAME, NEW_CLOSURE_VALUE(operator_store_null_silent));
     gravity_class_bind(gravity_class_null, GRAVITY_INTERNAL_NOTFOUND_NAME, NEW_CLOSURE_VALUE(operator_null_silent));
     #endif
-    gravity_class_bind(gravity_class_null, "iterate", NEW_CLOSURE_VALUE(null_iterator));
+    gravity_class_bind(gravity_class_null, ITERATOR_INIT_FUNCTION, NEW_CLOSURE_VALUE(null_iterator));
 
     // SYSTEM class
     gravity_class_system = gravity_class_new_pair(NULL, GRAVITY_CLASS_SYSTEM_NAME, NULL, 0, 0);

+ 35 - 21
src/runtime/gravity_vm.c

@@ -243,15 +243,16 @@ static inline gravity_callframe_t *gravity_new_callframe (gravity_vm *vm, gravit
     return &fiber->frames[fiber->nframes - 1];
 }
 
-static inline bool gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber, uint32_t rneeds, gravity_value_t **stackstart) {
+static inline bool gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber, uint32_t stacktopdelta, gravity_value_t **stackstart) {
     #pragma unused(vm)
-
+	if (stacktopdelta == 0) return true;
+	
     // update stacktop pointer before a call
-    fiber->stacktop += rneeds;
-
+    fiber->stacktop += stacktopdelta;
+	
     // check stack size
-    uint32_t stack_size = (uint32_t)(fiber->stacktop - fiber->stack);
-    uint32_t stack_needed = MAXNUM(stack_size + rneeds, DEFAULT_MINSTACK_SIZE);
+	uint32_t stack_size = (uint32_t)(fiber->stacktop - fiber->stack);
+    uint32_t stack_needed = MAXNUM(stack_size, DEFAULT_MINSTACK_SIZE);
     if (fiber->stackalloc >= stack_needed) return true;
     gravity_value_t *old_stack = fiber->stack;
 
@@ -261,7 +262,7 @@ static inline bool gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber,
     void *ptr = (size_condition) ? mem_realloc(NULL, fiber->stack, sizeof(gravity_value_t) * new_size) : NULL;
     if (!ptr) {
         // restore stacktop to previous state
-        fiber->stacktop -= rneeds;
+        fiber->stacktop -= stacktopdelta;
 
         // stack reallocation failed means that there is a very high probability to be into an infinite loop
         RUNTIME_ERROR("Infinite loop detected. Current execution must be aborted.");
@@ -477,7 +478,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     } break;
                 }
                 LOAD_FRAME();
-                SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin));
+                SYNC_STACKTOP(current_fiber, fiber, stacktopdelta);
 
                 // continue execution
                 DISPATCH();
@@ -617,7 +618,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     } break;
                 }
                 LOAD_FRAME();
-                SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin));
+                SYNC_STACKTOP(current_fiber, fiber, stacktopdelta);
 
                 // continue execution
                 DISPATCH();
@@ -1069,7 +1070,8 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                         // prepare func call
                         uint32_t rwin = FN_COUNTREG(func, frame->nargs);
                         uint32_t _rneed = FN_COUNTREG(closure->f, 1);
-                        if (!gravity_check_stack(vm, fiber, MAXNUM(_rneed, rwin), &stackstart)) return false;
+						uint32_t stacktopdelta = (uint32_t)MAXNUM(stackstart + rwin + _rneed - fiber->stacktop, 0);
+                        if (!gravity_check_stack(vm, fiber, stacktopdelta, &stackstart)) return false;
                         SETVALUE(rwin, v1);
 
                         // call func and check result
@@ -1104,7 +1106,8 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 // r2 is the register which contains the callable object
                 // r3 is the number of arguments (nparams)
 
-                // register window
+                // sliding register window as in:
+                // https://the-ravi-programming-language.readthedocs.io/en/latest/lua-parser.html#sliding-register-window-by-mike-pall
                 const uint32_t rwin = r2 + 1;
 
                 // object to call
@@ -1125,7 +1128,8 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 
                 // check stack size
                 uint32_t _rneed = FN_COUNTREG(closure->f, r3);
-                if (!gravity_check_stack(vm, fiber, MAXNUM(_rneed, rwin), &stackstart)) return false;
+				uint32_t stacktopdelta = (uint32_t)MAXNUM(stackstart + rwin + _rneed - fiber->stacktop, 0);
+                if (!gravity_check_stack(vm, fiber, stacktopdelta, &stackstart)) return false;
 
                 // if less arguments are passed then fill the holes with UNDEFINED values
                 while (r3 < closure->f->nparams) {
@@ -1217,7 +1221,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 }
                 
                 LOAD_FRAME();
-                SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin));
+                SYNC_STACKTOP(current_fiber, fiber, stacktopdelta);
 
                 DISPATCH();
             }
@@ -1246,7 +1250,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 // close open upvalues (if any)
                 gravity_close_upvalues(fiber, stackstart);
 
-                // check if it was a gravity_vm_runfunc execution
+                // check if it was a gravity_vm_runclosure execution
                 if (frame->outloop) {
                     fiber->result = result;
                     return true;
@@ -1259,7 +1263,9 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                     fiber = fiber->caller;
                     vm->fiber = fiber;
                 } else {
-                    fiber->stacktop = frame->stackstart;
+                    // recompute stacktop based on last executed frame
+					gravity_callframe_t *lastframe = &fiber->frames[fiber->nframes-1];
+                    fiber->stacktop = lastframe->stackstart + FN_COUNTREG(lastframe->closure->f, lastframe->nargs);
                 }
 
                 LOAD_FRAME();
@@ -1588,7 +1594,8 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
     gravity_fiber_t     *fiber = vm->fiber;
     gravity_value_t     *stackstart = NULL;
     uint32_t            rwin = 0;
-
+	uint32_t			stacktopdelta = 0;
+	
     DEBUG_STACK();
 
     // self value is default to the context where the closure has been created (or set by the user)
@@ -1618,8 +1625,9 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
 
         // check stack size
         uint32_t _rneed = FN_COUNTREG(f,nparams+1);
-        if (!gravity_check_stack(vm, vm->fiber, MAXNUM(_rneed, rwin), &stackstart)) return false;
-
+		stacktopdelta = (uint32_t)MAXNUM(stackstart + rwin + _rneed - vm->fiber->stacktop, 0);
+        if (!gravity_check_stack(vm, vm->fiber, stacktopdelta, &stackstart)) return false;
+		
         // setup params (first param is self)
         SETVALUE(rwin, selfvalue);
         for (uint16_t i=0; i<nparams; ++i) {
@@ -1633,7 +1641,8 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
         // there are no execution frames when called outside main function
         gravity_fiber_reassign(vm->fiber, closure, nparams+1);
         stackstart = vm->fiber->stack;
-
+		stacktopdelta = FN_COUNTREG(closure->f, nparams+1);
+		
         // setup params (first param is self)
         SETVALUE(rwin, selfvalue);
         for (uint16_t i=0; i<nparams; ++i) {
@@ -1680,8 +1689,13 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
             result = false;
             break;
     }
-    if (f->tag != EXEC_TYPE_NATIVE) --fiber->nframes;
-
+    
+    if (fiber == vm->fiber) {
+        // fix pointers ONLY if fiber remains the same
+        if (f->tag != EXEC_TYPE_NATIVE) --fiber->nframes;
+        fiber->stacktop -= stacktopdelta;
+    }
+	
     DEBUG_STACK();
     return result;
 }

+ 6 - 5
src/runtime/gravity_vmmacros.h

@@ -37,7 +37,7 @@
 #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_STRESSTEST           0               // 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!
@@ -232,9 +232,10 @@
 
 #define PREPARE_FUNC_CALLN(_c,_i,_w,_N)             gravity_closure_t *_c = (gravity_closure_t *)gravity_class_lookup_closure(gravity_value_getclass(v2), cache[_i]); \
                                                     if (!_c || !_c->f) RUNTIME_ERROR("Unable to perform operator %s on object", opcode_name(op));   \
-                                                    uint32_t _w = FN_COUNTREG(func, frame->nargs);                                                  \
-                                                    uint32_t _rneed = FN_COUNTREG(_c->f, _N);                                                       \
-                                                    if (!gravity_check_stack(vm, fiber, MAXNUM(_rneed,_w), &stackstart)) return false;              \
+                                                    uint32_t _w = FN_COUNTREG(func, frame->nargs); \
+                                                    uint32_t _rneed = FN_COUNTREG(_c->f, _N);      \
+													uint32_t stacktopdelta = (uint32_t)MAXNUM(stackstart + _w + _rneed - fiber->stacktop, 0); \
+                                                    if (!gravity_check_stack(vm, fiber, stacktopdelta, &stackstart)) return false;              \
                                                     if (vm->aborted) return false
 
 #define PREPARE_FUNC_CALL1(_c,_v1,_i,_w)            PREPARE_FUNC_CALLN(_c,_i,_w,1);         \
@@ -289,7 +290,7 @@
                                                         break;                                                          \
                                                     }                                                                   \
                                                     LOAD_FRAME();                                                       \
-                                                    SYNC_STACKTOP(current_fiber, fiber, MAXNUM(_rneed, rwin))
+                                                    SYNC_STACKTOP(current_fiber, fiber, stacktopdelta)
 
 // MACROS used in core and optionals
 #define SETMETA_INITED(c)                           gravity_class_get_meta(c)->is_inited = true

+ 6 - 8
src/shared/gravity_value.c

@@ -197,19 +197,16 @@ gravity_class_t *gravity_class_new_pair (gravity_vm *vm, const char *identifier,
 
     char buffer[512];
     snprintf(buffer, sizeof(buffer), "%s meta", identifier);
-
-    gravity_class_t *supermeta = (superclass) ? gravity_class_get_meta(superclass) : NULL;
-    uint32_t n1 = (supermeta) ? supermeta->nivars : 0;
-
-    gravity_class_t *meta = gravity_class_new_single(vm, buffer, nsvar + n1);
+    
+    // ivar count/grow is managed by gravity_class_setsuper
+    gravity_class_t *meta = gravity_class_new_single(vm, buffer, nsvar);
     meta->objclass = gravity_class_object;
     gravity_class_setsuper(meta, gravity_class_class);
 
-    uint32_t n2 = (superclass) ? superclass->nivars : 0;
-    gravity_class_t *c = gravity_class_new_single(vm, identifier, nivar + n2);
+    gravity_class_t *c = gravity_class_new_single(vm, identifier, nivar);
     c->objclass = meta;
 
-    // a class without a superclass in a subclass of Object.
+    // a class without a superclass in a subclass of Object
     gravity_class_setsuper(c, (superclass) ? superclass : gravity_class_object);
 
     return c;
@@ -1391,6 +1388,7 @@ void gravity_fiber_blacken (gravity_vm *vm, gravity_fiber_t *fiber) {
     // gray call frame functions
     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].args);
     }
 
     // gray stack variables

+ 2 - 2
src/shared/gravity_value.h

@@ -66,8 +66,8 @@
 extern "C" {
 #endif
 
-#define GRAVITY_VERSION						"0.5.9"     // git tag 0.5.9
-#define GRAVITY_VERSION_NUMBER				0x000509    // git push --tags
+#define GRAVITY_VERSION						"0.6.0"     // git tag 0.6.0
+#define GRAVITY_VERSION_NUMBER				0x000600    // git push --tags
 #define GRAVITY_BUILD_DATE                  __DATE__
 
 #ifndef GRAVITY_ENABLE_DOUBLE

+ 1 - 0
src/utils/gravity_utils.c

@@ -9,6 +9,7 @@
 #include <time.h>
 #include <fcntl.h>
 #include <ctype.h>
+#include <stdio.h>
 #include <string.h>
 #include <assert.h>
 #include <unistd.h>

+ 40 - 0
test/unittest/anon_class_test.gravity

@@ -0,0 +1,40 @@
+#unittest {
+    name: "Anonimous class test";
+    result: 666
+};
+
+var Window1;
+
+class _Window1 {
+    var button;
+    
+    class _Button1 {
+        func test() {
+            return 777;
+        }
+    }
+    
+    func setup() {
+        button = _Button1();
+        button.bind("action", {var a = Window1.action(); return a;})
+    }
+    
+    func action() {
+        return 666;
+    }
+}
+
+func main() {
+    Window1 = _Window1();
+    Window1.setup();
+    
+    var test = Window1.action();    
+    var button = Window1.button;
+    button.test();
+    var temp = button.action();
+    
+    // force GC to run
+    var l = [1,2,3];
+    
+    return temp;
+}

+ 13 - 0
test/unittest/map_iterate_nokeys.gravity

@@ -0,0 +1,13 @@
+#unittest {
+	name: "Map iterate no keys (to test infinite loop issue).";
+	result: 0;
+};
+
+func main() {
+	var sum = 0;
+	var map = ["Marco":10, "Andrea":20, "Chiara":30];
+	for (var key in map) {
+		sum += map[key];
+	}
+	return sum;
+}

+ 35 - 0
test/unittest/super_class_test.gravity

@@ -0,0 +1,35 @@
+#unittest {
+    name: "Super class test";
+    result: 2100
+};
+
+class c1 {
+    var v1;
+}
+
+class c2 : c1 {
+    var v2;
+    var v3;
+}
+
+class c3 : c2 {
+    var v4;
+    var v5;
+    var v6;
+    
+    func sum() {
+        return v1+v2+v3+v4+v5+v6;
+    }
+}
+
+func main() {
+    var c = c3();
+    c.v1 = 100;
+    c.v2 = 200;
+    c.v3 = 300;
+    c.v4 = 400;
+    c.v5 = 500;
+    c.v6 = 600;
+    
+    return c.sum();
+}