Browse Source

Added a way to control max recursion depth (unit test added).

Marco Bambini 7 years ago
parent
commit
244c758498
4 changed files with 64 additions and 30 deletions
  1. 14 12
      src/runtime/gravity_core.c
  2. 30 12
      src/runtime/gravity_vm.c
  3. 7 6
      src/runtime/gravity_vm.h
  4. 13 0
      test/unittest/max_recursion.gravity

+ 14 - 12
src/runtime/gravity_core.c

@@ -3046,12 +3046,13 @@ static void gravity_core_init (void) {
 
     closure = computed_property_create(NULL, NEW_FUNCTION(system_get), NEW_FUNCTION(system_set));
 	gravity_value_t value = VALUE_FROM_OBJECT(closure);
-	gravity_class_bind(system_meta, GRAVITY_VM_GCENABLED_KEY, value);
-	gravity_class_bind(system_meta, GRAVITY_VM_GCMINTHRESHOLD_KEY, value);
-	gravity_class_bind(system_meta, GRAVITY_VM_GCTHRESHOLD_KEY, value);
-	gravity_class_bind(system_meta, GRAVITY_VM_GCRATIO_KEY, value);
-    gravity_class_bind(system_meta, GRAVITY_VM_MAXCALLS_KEY, value);
-    gravity_class_bind(system_meta, GRAVITY_VM_MAXBLOCK_KEY, value);
+	gravity_class_bind(system_meta, GRAVITY_VM_GCENABLED, value);
+	gravity_class_bind(system_meta, GRAVITY_VM_GCMINTHRESHOLD, value);
+	gravity_class_bind(system_meta, GRAVITY_VM_GCTHRESHOLD, value);
+	gravity_class_bind(system_meta, GRAVITY_VM_GCRATIO, value);
+    gravity_class_bind(system_meta, GRAVITY_VM_MAXCALLS, value);
+    gravity_class_bind(system_meta, GRAVITY_VM_MAXBLOCK, value);
+    gravity_class_bind(system_meta, GRAVITY_VM_MAXRECURSION, value);
 
 	// INIT META
 	SETMETA_INITED(gravity_class_int);
@@ -3098,7 +3099,7 @@ void gravity_core_free (void) {
     computed_property_free(gravity_class_float, "radians", true);
     computed_property_free(gravity_class_float, "degrees", true);
     gravity_class_t *system_meta = gravity_class_get_meta(gravity_class_system);
-    computed_property_free(system_meta, GRAVITY_VM_GCENABLED_KEY, true);
+    computed_property_free(system_meta, GRAVITY_VM_GCENABLED, true);
 
 	gravity_class_free_core(NULL, gravity_class_get_meta(gravity_class_int));
 	gravity_class_free_core(NULL, gravity_class_int);
@@ -3128,11 +3129,12 @@ void gravity_core_free (void) {
 	gravity_class_free_core(NULL, gravity_class_upvalue);
 
 	// before freeing the meta class we need to remove entries with duplicated functions
-	{STATICVALUE_FROM_STRING(key, GRAVITY_VM_GCMINTHRESHOLD_KEY, strlen(GRAVITY_VM_GCMINTHRESHOLD_KEY)); gravity_hash_remove(system_meta->htable, key);}
-	{STATICVALUE_FROM_STRING(key, GRAVITY_VM_GCTHRESHOLD_KEY, strlen(GRAVITY_VM_GCTHRESHOLD_KEY)); gravity_hash_remove(system_meta->htable, key);}
-	{STATICVALUE_FROM_STRING(key, GRAVITY_VM_GCRATIO_KEY, strlen(GRAVITY_VM_GCRATIO_KEY)); gravity_hash_remove(system_meta->htable, key);}
-    {STATICVALUE_FROM_STRING(key, GRAVITY_VM_MAXCALLS_KEY, strlen(GRAVITY_VM_MAXCALLS_KEY)); gravity_hash_remove(system_meta->htable, key);}
-    {STATICVALUE_FROM_STRING(key, GRAVITY_VM_MAXBLOCK_KEY, strlen(GRAVITY_VM_MAXBLOCK_KEY)); gravity_hash_remove(system_meta->htable, key);}
+	{STATICVALUE_FROM_STRING(key, GRAVITY_VM_GCMINTHRESHOLD, strlen(GRAVITY_VM_GCMINTHRESHOLD)); gravity_hash_remove(system_meta->htable, key);}
+	{STATICVALUE_FROM_STRING(key, GRAVITY_VM_GCTHRESHOLD, strlen(GRAVITY_VM_GCTHRESHOLD)); gravity_hash_remove(system_meta->htable, key);}
+	{STATICVALUE_FROM_STRING(key, GRAVITY_VM_GCRATIO, strlen(GRAVITY_VM_GCRATIO)); gravity_hash_remove(system_meta->htable, key);}
+    {STATICVALUE_FROM_STRING(key, GRAVITY_VM_MAXCALLS, strlen(GRAVITY_VM_MAXCALLS)); gravity_hash_remove(system_meta->htable, key);}
+    {STATICVALUE_FROM_STRING(key, GRAVITY_VM_MAXBLOCK, strlen(GRAVITY_VM_MAXBLOCK)); gravity_hash_remove(system_meta->htable, key);}
+    {STATICVALUE_FROM_STRING(key, GRAVITY_VM_MAXRECURSION, strlen(GRAVITY_VM_MAXRECURSION)); gravity_hash_remove(system_meta->htable, key);}
 	gravity_class_free_core(NULL, system_meta);
 	gravity_class_free_core(NULL, gravity_class_system);
 

+ 30 - 12
src/runtime/gravity_vm.c

@@ -43,6 +43,10 @@ struct gravity_vm {
     uint32_t            maxccalls;                          // maximum number of nested c calls
     uint32_t            nccalls;                            // current number of nested c calls
 
+    // recursion
+    gravity_int_t       maxrecursion;                       // maximum recursive depth
+    gravity_int_t       recursioncount;                     // recustion counter
+    
 	// anonymous names
 	uint32_t			nanon;								// counter for anonymous classes (used in object_bind)
 	char				temp[64];							// temprary buffer used for anonymous names generator
@@ -1109,6 +1113,16 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                             }
                         }
 						PUSH_FRAME(closure, &stackstart[rwin], r1, r3);
+                        
+                        // max depth recursion check
+                        if (vm->maxrecursion != 0) {
+                            if (func != closure->f) vm->recursioncount = 0;
+                            else if (++vm->recursioncount >= vm->maxrecursion) {
+                                const char *identifier = (func->identifier) ? func->identifier : "anon";
+                                RUNTIME_ERROR("Max recursion depth exceeded for func %s (limit is set to %d)", identifier , vm->maxrecursion);
+                                break;
+                            }
+                        }
 					} break;
 
 					case EXEC_TYPE_INTERNAL: {
@@ -1152,6 +1166,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 						RUNTIME_ERROR("Unable to handle a special function in current context");
 						break;
 				}
+                
 				LOAD_FRAME();
 				SYNC_STACKTOP(current_fiber, MAXNUM(_rneed, rwin));
 
@@ -1370,6 +1385,7 @@ gravity_vm *gravity_vm_new (gravity_delegate_t *delegate) {
 	// allocate default fiber
 	vm->fiber = gravity_fiber_new(vm, NULL, 0, 0);
     vm->maxccalls = MAX_CCALLS;
+    vm->maxrecursion = 0; // default is no limit
 
 	vm->pc = 0;
     vm->delegate = (delegate) ? delegate : &empty_delegate;
@@ -1718,24 +1734,26 @@ gravity_int_t gravity_vm_maxmemblock (gravity_vm *vm) {
 
 gravity_value_t gravity_vm_get (gravity_vm *vm, const char *key) {
 	if (key) {
-		if (strcmp(key, GRAVITY_VM_GCENABLED_KEY) == 0) return VALUE_FROM_BOOL(vm->gcenabled);
-		if (strcmp(key, GRAVITY_VM_GCMINTHRESHOLD_KEY) == 0) return VALUE_FROM_INT(vm->gcminthreshold);
-		if (strcmp(key, GRAVITY_VM_GCTHRESHOLD_KEY) == 0) return VALUE_FROM_INT(vm->gcthreshold);
-		if (strcmp(key, GRAVITY_VM_GCRATIO_KEY) == 0) return VALUE_FROM_FLOAT(vm->gcratio);
-        if (strcmp(key, GRAVITY_VM_MAXCALLS_KEY) == 0) return VALUE_FROM_INT(vm->maxccalls);
-        if (strcmp(key, GRAVITY_VM_MAXBLOCK_KEY) == 0) return VALUE_FROM_INT(vm->maxmemblock);
+		if (strcmp(key, GRAVITY_VM_GCENABLED) == 0) return VALUE_FROM_BOOL(vm->gcenabled);
+		if (strcmp(key, GRAVITY_VM_GCMINTHRESHOLD) == 0) return VALUE_FROM_INT(vm->gcminthreshold);
+		if (strcmp(key, GRAVITY_VM_GCTHRESHOLD) == 0) return VALUE_FROM_INT(vm->gcthreshold);
+		if (strcmp(key, GRAVITY_VM_GCRATIO) == 0) return VALUE_FROM_FLOAT(vm->gcratio);
+        if (strcmp(key, GRAVITY_VM_MAXCALLS) == 0) return VALUE_FROM_INT(vm->maxccalls);
+        if (strcmp(key, GRAVITY_VM_MAXBLOCK) == 0) return VALUE_FROM_INT(vm->maxmemblock);
+        if (strcmp(key, GRAVITY_VM_MAXRECURSION) == 0) return VALUE_FROM_INT(vm->maxrecursion);
 	}
 	return VALUE_FROM_NULL;
 }
 
 bool gravity_vm_set (gravity_vm *vm, const char *key, gravity_value_t value) {
 	if (key) {
-		if ((strcmp(key, GRAVITY_VM_GCENABLED_KEY) == 0) && VALUE_ISA_BOOL(value)) {vm->gcenabled = VALUE_AS_BOOL(value); return true;}
-		if ((strcmp(key, GRAVITY_VM_GCMINTHRESHOLD_KEY) == 0) && VALUE_ISA_INT(value)) {vm->gcminthreshold = VALUE_AS_INT(value); return true;}
-		if ((strcmp(key, GRAVITY_VM_GCTHRESHOLD_KEY) == 0) && VALUE_ISA_INT(value)) {vm->gcthreshold = VALUE_AS_INT(value); return true;}
-		if ((strcmp(key, GRAVITY_VM_GCRATIO_KEY) == 0) && VALUE_ISA_FLOAT(value)) {vm->gcratio = VALUE_AS_FLOAT(value); return true;}
-        if ((strcmp(key, GRAVITY_VM_MAXCALLS_KEY) == 0) && VALUE_ISA_INT(value)) {vm->maxccalls = (uint32_t)VALUE_AS_INT(value); return true;}
-        if ((strcmp(key, GRAVITY_VM_MAXBLOCK_KEY) == 0) && VALUE_ISA_INT(value)) {vm->maxmemblock = (uint32_t)VALUE_AS_INT(value); return true;}
+		if ((strcmp(key, GRAVITY_VM_GCENABLED) == 0) && VALUE_ISA_BOOL(value)) {vm->gcenabled = VALUE_AS_BOOL(value); return true;}
+		if ((strcmp(key, GRAVITY_VM_GCMINTHRESHOLD) == 0) && VALUE_ISA_INT(value)) {vm->gcminthreshold = VALUE_AS_INT(value); return true;}
+		if ((strcmp(key, GRAVITY_VM_GCTHRESHOLD) == 0) && VALUE_ISA_INT(value)) {vm->gcthreshold = VALUE_AS_INT(value); return true;}
+		if ((strcmp(key, GRAVITY_VM_GCRATIO) == 0) && VALUE_ISA_FLOAT(value)) {vm->gcratio = VALUE_AS_FLOAT(value); return true;}
+        if ((strcmp(key, GRAVITY_VM_MAXCALLS) == 0) && VALUE_ISA_INT(value)) {vm->maxccalls = (uint32_t)VALUE_AS_INT(value); return true;}
+        if ((strcmp(key, GRAVITY_VM_MAXBLOCK) == 0) && VALUE_ISA_INT(value)) {vm->maxmemblock = (uint32_t)VALUE_AS_INT(value); return true;}
+        if ((strcmp(key, GRAVITY_VM_MAXRECURSION) == 0) && VALUE_ISA_INT(value)) {vm->maxrecursion = (uint32_t)VALUE_AS_INT(value); return true;}
 	}
 	return false;
 }

+ 7 - 6
src/runtime/gravity_vm.h

@@ -16,12 +16,13 @@
 extern "C" {
 #endif
 
-#define GRAVITY_VM_GCENABLED_KEY            "gcenabled"
-#define GRAVITY_VM_GCMINTHRESHOLD_KEY       "gcminthreshold"
-#define GRAVITY_VM_GCTHRESHOLD_KEY          "gcthreshold"
-#define GRAVITY_VM_GCRATIO_KEY              "gcratio"
-#define GRAVITY_VM_MAXCALLS_KEY             "maxcalls"
-#define GRAVITY_VM_MAXBLOCK_KEY             "maxblock"
+#define GRAVITY_VM_GCENABLED            "gcEnabled"
+#define GRAVITY_VM_GCMINTHRESHOLD       "gcMinThreshold"
+#define GRAVITY_VM_GCTHRESHOLD          "gcThreshold"
+#define GRAVITY_VM_GCRATIO              "gcRatio"
+#define GRAVITY_VM_MAXCALLS             "maxCCalls"
+#define GRAVITY_VM_MAXBLOCK             "maxBlock"
+#define GRAVITY_VM_MAXRECURSION         "maxRecursionDepth"
 
 typedef bool (*vm_filter_cb) (gravity_object_t *obj);
 typedef void (*vm_transfer_cb) (gravity_vm *vm, gravity_object_t *obj);

+ 13 - 0
test/unittest/max_recursion.gravity

@@ -0,0 +1,13 @@
+#unittest {
+    name: "Max recursion";
+    error: RUNTIME;
+};
+
+func f(a) {
+    return f(a-1);
+}
+
+func main() {
+    System.maxRecursionDepth = 1024;
+    return f(0);
+}