Bläddra i källkod

Fixed an issue with super keyword. Unit test added.

Marco Bambini 6 år sedan
förälder
incheckning
d06f15ef47

+ 47 - 8
src/compiler/gravity_codegen.c

@@ -252,21 +252,38 @@ static void fix_superclasses (gvisitor_t *self) {
         gnode_class_decl_t *node = (gnode_class_decl_t *)gnode_array_get(superfix, i);
         gnode_class_decl_t *super = (gnode_class_decl_t *)node->superclass;
 
-        gravity_class_t    *c = (gravity_class_t *)node->data;
+        gravity_class_t *c = (gravity_class_t *)node->data;
         gravity_class_setsuper(c, (gravity_class_t *)super->data);
     }
 }
 
-static bool context_is_class (gvisitor_t *self) {
+static const char *lookup_superclass_identifier (gvisitor_t *self, gravity_class_t *c) {
+    codegen_t       *data = (codegen_t *)self->data;
+    gnode_class_r   *superfix = &data->superfix;
+    
+    size_t count = gnode_array_size(superfix);
+    for (size_t i=0; i<count; ++i) {
+        gnode_class_decl_t *node = (gnode_class_decl_t *)gnode_array_get(superfix, i);
+        gravity_class_t *target = (gravity_class_t *)node->data;
+        if (target == c) {
+            gnode_class_decl_t *super = (gnode_class_decl_t *)node->superclass;
+            return (super) ? super->identifier : NULL;
+        }
+    }
+    
+    return NULL;
+}
+
+static gravity_class_t *context_get_class (gvisitor_t *self) {
     gravity_object_r ctx = ((codegen_t *)self->data)->context;
     size_t len = marray_size(ctx);
     
     for (int i=(int)len-1; i>=0; --i) {
         gravity_object_t *context_object = (gravity_object_t *)marray_get(ctx, i);
-        if (OBJECT_ISA_CLASS(context_object)) return true;
+        if (OBJECT_ISA_CLASS(context_object)) return (gravity_class_t *)context_object;
     }
     
-    return false;
+    return NULL;
 }
 
 static bool node_is_closure (gnode_t *node, uint16_t *local_index) {
@@ -333,7 +350,7 @@ static uint32_t compute_self_register (gvisitor_t *self, ircode_t *code, gnode_t
     uint16_t local_index = 0;
     if (node_is_closure(node, &local_index)) {
         if (next_is_access) return local_index;
-        if (context_is_class(self)) return 0;
+        if (context_get_class(self) != NULL) return 0;
     }
     
     // default case is to return the target register
@@ -1191,6 +1208,7 @@ static void visit_class_decl (gvisitor_t *self, gnode_class_decl_t *node) {
                 // superclass has not yet processed so we need recheck the node at the end of the visit
                 // add node to superfix for later processing
                 codegen_t *data = (codegen_t *)self->data;
+                node->data = (void *)c;
                 marray_push(gnode_class_decl_t *, data->superfix, node);
             }
         }
@@ -1494,8 +1512,29 @@ static void visit_postfix_expr (gvisitor_t *self, gnode_postfix_expr_t *node) {
                 return;
             }
 
-			if (is_super) ircode_add(code, LOADS, dest_register, target_register, index_register, LINE_NUMBER(node));
-			else ircode_add(code, (is_real_assigment) ? STORE : LOAD, dest_register, target_register, index_register, LINE_NUMBER(node));
+            if (is_super) {
+                gravity_class_t *class = context_get_class(self);
+                if (!class) {
+                    report_error(self, (gnode_t *)node, "Unable to use super keyword in a non class context.");
+                    return;
+                }
+                
+                // check if class has a superclass not yet processed
+                const char *identifier = lookup_superclass_identifier (self, class);
+                if (!identifier) {
+                    // it means that class superclass has already been processed
+                    class = class->superclass;
+                    identifier = (class) ? class->identifier : GRAVITY_CLASS_OBJECT_NAME;
+                }
+                
+                uint32_t cpool_index = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, identifier));
+                ircode_add_constant(code, cpool_index, LINE_NUMBER(node));
+                uint32_t temp_reg = ircode_register_pop(code);
+                ircode_add(code, LOADS, dest_register, temp_reg, index_register, LINE_NUMBER(node));
+            } else {
+                ircode_add(code, (is_real_assigment) ? STORE : LOAD, dest_register, target_register, index_register, LINE_NUMBER(node));
+            }
+            
             if (!is_real_assigment) {
                 if (i+1<count) {
                 uint32_t rtemp = ircode_register_pop_context_protect(code, true);
@@ -1817,7 +1856,7 @@ static void visit_keyword_expr (gvisitor_t *self, gnode_keyword_expr_t *node) {
             break;
 
         case TOK_KEY_SUPER:
-			ircode_add_constant(code, CPOOL_VALUE_SUPER, LINE_NUMBER(node));
+            ircode_register_push(code, 0);
             break;
 
         case TOK_KEY_CURRARGS:

+ 18 - 32
src/runtime/gravity_core.c

@@ -651,13 +651,13 @@ static bool object_not (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     RETURN_VALUE(VALUE_FROM_BOOL(VALUE_ISA_NULLCLASS(GET_VALUE(0))), rindex);
 }
 
-static bool object_real_load (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex, bool is_super) {
+static bool object_load (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     #pragma unused(vm, nargs)
-
+    
     // if there is a possibility that gravity_vm_runclosure is called then it is MANDATORY to save arguments before the call
     gravity_value_t target = GET_VALUE(0);
     gravity_value_t key = GET_VALUE(1);
-
+    
     // check if meta class needs to be initialized (it means if it contains valued static ivars)
     // meta classes must be inited somewhere, this problem does not exist with instances since object creation itself trigger a class init
     if (VALUE_ISA_CLASS(target)) {
@@ -671,32 +671,27 @@ static bool object_real_load (gravity_vm *vm, gravity_value_t *args, uint16_t na
             }
         }
     }
-
-    // sanity check for super operator
-    if (is_super && !VALUE_ISA_CLASS(target)) {
-        RETURN_ERROR("Unable to lookup super for non class object");
-    }
-
+    
     // retrieve class and process key
-    gravity_class_t *c = (is_super) ? VALUE_AS_CLASS(target) : gravity_value_getclass(target);
+    gravity_class_t *c = gravity_value_getclass(target);
     gravity_instance_t *instance = VALUE_ISA_INSTANCE(target) ? VALUE_AS_INSTANCE(target) : NULL;
-
+    
     // key is an int its an optimization for faster loading of ivar
     if (VALUE_ISA_INT(key)) {
         // sanity check
         uint32_t nivar = c->nivars;
         uint32_t nindex = (uint32_t)key.n;
         if (nindex >= nivar) RETURN_ERROR("Out of bounds ivar index in load operation (1).");
-
+        
         if (instance) RETURN_VALUE(instance->ivars[nindex], rindex);    // instance case
         RETURN_VALUE(c->ivars[nindex], rindex);                         // class case
     }
-
+    
     // key must be a string in this version
     if (!VALUE_ISA_STRING(key)) {
         RETURN_ERROR("Unable to lookup non string value into class %s", c->identifier);
     }
-
+    
     // lookup key in class c
     gravity_object_t *obj = (gravity_object_t *)gravity_class_lookup(c, key);
     if (!obj) {
@@ -707,11 +702,11 @@ static bool object_real_load (gravity_vm *vm, gravity_value_t *args, uint16_t na
         }
     }
     if (!obj) goto execute_notfound;
-
+    
     if (OBJECT_ISA_CLOSURE(obj)) {
         gravity_closure_t *closure = (gravity_closure_t *)obj;
         if (!closure || !closure->f) goto execute_notfound;
-
+        
         // execute optimized default getter
         if (FUNCTION_ISA_SPECIAL(closure->f)) {
             if (FUNCTION_ISA_DEFAULT_GETTER(closure->f)) {
@@ -719,11 +714,11 @@ static bool object_real_load (gravity_vm *vm, gravity_value_t *args, uint16_t na
                 uint32_t nivar = c->nivars;
                 uint32_t nindex = closure->f->index;
                 if (nindex >= nivar) RETURN_ERROR("Out of bounds ivar index in load operation (2).");
-
+                
                 if (instance) RETURN_VALUE(instance->ivars[closure->f->index], rindex);
                 RETURN_VALUE(c->ivars[closure->f->index], rindex);
             }
-
+            
             if (FUNCTION_ISA_GETTER(closure->f)) {
                 // returns a function to be executed using the return false trick
                 RETURN_CLOSURE(VALUE_FROM_OBJECT((gravity_closure_t *)closure->f->special[EXEC_TYPE_SPECIAL_GETTER]), rindex);
@@ -731,22 +726,14 @@ static bool object_real_load (gravity_vm *vm, gravity_value_t *args, uint16_t na
             goto execute_notfound;
         }
     }
-
+    
     RETURN_VALUE(VALUE_FROM_OBJECT(obj), rindex);
-
+    
 execute_notfound: {
-        // in case of not found error return the notfound function to be executed (MANDATORY)
-        gravity_closure_t *closure = (gravity_closure_t *)gravity_class_lookup(c, gravity_vm_keyindex(vm, GRAVITY_NOTFOUND_INDEX));
-        RETURN_CLOSURE(VALUE_FROM_OBJECT(closure), rindex);
-    }
-}
-
-static bool object_loads (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-    return object_real_load(vm, args, nargs, rindex, true);
+    // in case of not found error return the notfound function to be executed (MANDATORY)
+    gravity_closure_t *closure = (gravity_closure_t *)gravity_class_lookup(c, gravity_vm_keyindex(vm, GRAVITY_NOTFOUND_INDEX));
+    RETURN_CLOSURE(VALUE_FROM_OBJECT(closure), rindex);
 }
-
-static bool object_load (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-    return object_real_load(vm, args, nargs, rindex, false);
 }
 
 static bool object_store (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -3279,7 +3266,6 @@ void gravity_core_init (void) {
     gravity_class_bind(gravity_class_object, GRAVITY_CLASS_BOOL_NAME, NEW_CLOSURE_VALUE(convert_object_bool));
     gravity_class_bind(gravity_class_object, GRAVITY_CLASS_STRING_NAME, NEW_CLOSURE_VALUE(convert_object_string));
     gravity_class_bind(gravity_class_object, GRAVITY_INTERNAL_LOAD_NAME, NEW_CLOSURE_VALUE(object_load));
-    gravity_class_bind(gravity_class_object, GRAVITY_INTERNAL_LOADS_NAME, NEW_CLOSURE_VALUE(object_loads));
     gravity_class_bind(gravity_class_object, GRAVITY_INTERNAL_STORE_NAME, NEW_CLOSURE_VALUE(object_store));
     gravity_class_bind(gravity_class_object, GRAVITY_INTERNAL_NOTFOUND_NAME, NEW_CLOSURE_VALUE(object_notfound));
     gravity_class_bind(gravity_class_object, "_size", NEW_CLOSURE_VALUE(object_internal_size));

+ 24 - 2
src/runtime/gravity_vm.c

@@ -411,12 +411,34 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 SETVALUE(r1, STACK_GET(r2));
                 DISPATCH();
             }
+            
+            // MARK: LOADS
+            CASE_CODE(LOADS): {
+                OPCODE_GET_TWO8bit_ONE10bit(inst, const uint32_t r1, const uint32_t r2, const uint32_t r3);
+                DEBUG_VM("LOADS %d %d %d", r1, r2, r3);
+                
+                // r1 result
+                // r2 superclass to lookup (target implicit to self)
+                // r3 key
+                
+                DEFINE_STACK_VARIABLE(v2,r2);
+                DEFINE_INDEX_VARIABLE(v3,r3);
+                
+                gravity_value_t target = STACK_GET(0);
+                gravity_class_t *target_class = gravity_value_getclass(target);
+                gravity_class_t *super_target = gravity_class_lookup_class_identifier(target_class, VALUE_AS_CSTRING(v2));
+                if (!super_target) RUNTIME_ERROR("Unable to find superclass %s in self object", VALUE_AS_CSTRING(v2));
+                
+                gravity_object_t *result = gravity_class_lookup(super_target, v3);
+                if (!result) RUNTIME_ERROR("Unable to find %s in superclass %s", VALUE_AS_CSTRING(v3), (super_target->identifier) ? super_target->identifier : "N/A");
+                
+                SETVALUE(r1, VALUE_FROM_OBJECT(result));
+                DISPATCH();
+            }
 
             // MARK: LOAD
-            // MARK: LOADS
             // MARK: LOADAT
             CASE_CODE(LOAD):
-            CASE_CODE(LOADS):
             CASE_CODE(LOADAT):{
                 OPCODE_GET_TWO8bit_ONE10bit(inst, const uint32_t r1, const uint32_t r2, const uint32_t r3);
                 DEBUG_VM("%s %d %d %d", (op == LOAD) ? "LOAD" : ((op == LOADAT) ? "LOADAT" : "LOADS"), r1, r2, r3);

+ 8 - 0
src/shared/gravity_value.c

@@ -432,6 +432,14 @@ inline gravity_object_t *gravity_class_lookup (gravity_class_t *c, gravity_value
     return NULL;
 }
 
+gravity_class_t *gravity_class_lookup_class_identifier (gravity_class_t *c, const char *identifier) {
+    while (c) {
+        if (string_cmp(c->identifier, identifier) == 0) return c;
+        c = c->superclass;
+    }
+    return NULL;
+}
+
 inline gravity_closure_t *gravity_class_lookup_closure (gravity_class_t *c, gravity_value_t key) {
     gravity_object_t *obj = gravity_class_lookup(c, key);
     if (obj && OBJECT_ISA_CLOSURE(obj)) return (gravity_closure_t *)obj;

+ 3 - 2
src/shared/gravity_value.h

@@ -66,8 +66,8 @@
 extern "C" {
 #endif
 
-#define GRAVITY_VERSION						"0.7.6"     // git tag 0.7.6
-#define GRAVITY_VERSION_NUMBER				0x000706    // git push --tags
+#define GRAVITY_VERSION						"0.7.7"     // git tag 0.7.7
+#define GRAVITY_VERSION_NUMBER				0x000707    // git push --tags
 #define GRAVITY_BUILD_DATE                  __DATE__
 
 #ifndef GRAVITY_ENABLE_DOUBLE
@@ -478,6 +478,7 @@ GRAVITY_API void                gravity_class_free_core (gravity_vm *vm, gravity
 GRAVITY_API gravity_object_t    *gravity_class_lookup (gravity_class_t *c, gravity_value_t key);
 GRAVITY_API gravity_closure_t   *gravity_class_lookup_closure (gravity_class_t *c, gravity_value_t key);
 GRAVITY_API gravity_closure_t   *gravity_class_lookup_constructor (gravity_class_t *c, uint32_t nparams);
+GRAVITY_API gravity_class_t     *gravity_class_lookup_class_identifier (gravity_class_t *c, const char *identifier);
 GRAVITY_API void                gravity_class_blacken (gravity_vm *vm, gravity_class_t *c);
 GRAVITY_API uint32_t            gravity_class_size (gravity_vm *vm, gravity_class_t *c);
 

+ 29 - 0
test/unittest/superclass_chain.gravity

@@ -0,0 +1,29 @@
+#unittest {
+	name: "Superclass init call chain.";
+	result: 1110;
+};
+
+var g = 0;
+
+class Parent {
+    func init() {
+        g += 1000;
+    }
+}
+class Child:Parent {
+    func init() {
+        g += 100;
+        super.init();
+    }
+}
+class Grandchild:Child {
+    func init() {
+        g += 10;
+        super.init();
+    }
+}
+
+func main() {
+    var node = Grandchild();
+    return g;
+}