Prechádzať zdrojové kódy

Fixed an issue with local class declarations. Fixed issue related to superclasses. Fixed continue keyword in for loops.

Marco Bambini 6 rokov pred
rodič
commit
5fbf65c9a1

+ 24 - 2
src/compiler/gravity_ast.c

@@ -96,6 +96,28 @@ gupvalue_t *gnode_function_add_upvalue(gnode_function_decl_t *f, gnode_var_t *sy
     return upvalue;
 }
 
+gnode_t *gnode2class (gnode_t *node, bool *isextern) {
+    if (isextern) *isextern = false;
+    
+    if (NODE_ISA_CLASS(node)) {
+        gnode_class_decl_t *c = (gnode_class_decl_t *)node;
+        if (isextern) *isextern = (c->storage == TOK_KEY_EXTERN);
+        return node;
+    }
+    else if (NODE_ISA(node, NODE_VARIABLE)) {
+        gnode_var_t *var = (gnode_var_t *)node;
+        const char *class_manifest_type = gravity_class_class->identifier;
+        if ((var->annotation_type) && (string_cmp(var->annotation_type, class_manifest_type) == 0) && (NODE_ISA_CLASS(var->expr))) return var->expr;
+        
+        gnode_variable_decl_t *vdecl = var->vdecl;
+        if (vdecl && isextern && (vdecl->storage == TOK_KEY_EXTERN)) {
+            *isextern = true;
+            return node;
+        }
+    }
+    return NULL;
+}
+
 // MARK: - Statements initializers -
 
 gnode_t *gnode_jump_stat_create (gtoken_s token, gnode_t *expr, gnode_t *decl) {
@@ -236,7 +258,7 @@ gnode_t *gnode_variable_decl_create (gtoken_s token, gtoken_t type, gtoken_t acc
     return (gnode_t *)node;
 }
 
-gnode_t *gnode_variable_create (gtoken_s token, const char *identifier, const char *annotation_type, gtoken_t access_specifier, gnode_t *expr, gnode_t *decl) {
+gnode_t *gnode_variable_create (gtoken_s token, const char *identifier, const char *annotation_type, gnode_t *expr, gnode_t *decl, gnode_variable_decl_t *vdecl) {
     gnode_var_t *node = (gnode_var_t *)mem_alloc(NULL, sizeof(gnode_var_t));
 
     SETBASE(node, NODE_VARIABLE, token);
@@ -244,7 +266,7 @@ gnode_t *gnode_variable_create (gtoken_s token, const char *identifier, const ch
     node->identifier = identifier;
     node->annotation_type = annotation_type;
     node->expr = expr;
-    node->access = access_specifier;
+    node->vdecl = vdecl;
     node->iscomputed = false;
     return (gnode_t *)node;
 }

+ 12 - 9
src/compiler/gravity_ast.h

@@ -128,6 +128,14 @@ typedef struct {
 } gnode_function_decl_t;
 typedef gnode_function_decl_t gnode_function_expr_t;
 
+typedef struct {
+    gnode_t             base;               // VARIABLE_DECL
+    gtoken_t            type;               // TOK_KEY_VAR | TOK_KEY_CONST
+    gtoken_t            access;             // TOK_KEY_PRIVATE | TOK_KEY_INTERNAL | TOK_KEY_PUBLIC
+    gtoken_t            storage;            // TOK_KEY_STATIC | TOK_KEY_EXTERN
+    gnode_r             *decls;             // variable declarations list (gnode_var_t)
+} gnode_variable_decl_t;
+
 typedef struct {
     gnode_t             base;               // VARIABLE
     gnode_t             *env;               // shortcut to node where variable is declared
@@ -138,16 +146,9 @@ typedef struct {
     uint16_t            index;              // local variable index (if local)
     bool                upvalue;            // flag set if this variable is used as an upvalue
     bool                iscomputed;         // flag set is variable must not be backed
+    gnode_variable_decl_t   *vdecl;         // reference to enclosing variable declaration (in order to be able to have access to storage and access fields)
 } gnode_var_t;
 
-typedef struct {
-    gnode_t             base;               // VARIABLE_DECL
-    gtoken_t            type;               // TOK_KEY_VAR | TOK_KEY_CONST
-    gtoken_t            access;             // TOK_KEY_PRIVATE | TOK_KEY_INTERNAL | TOK_KEY_PUBLIC
-    gtoken_t            storage;            // TOK_KEY_STATIC | TOK_KEY_EXTERN
-    gnode_r             *decls;             // variable declarations list (gnode_var_t)
-} gnode_variable_decl_t;
-
 typedef struct {
     gnode_t             base;               // ENUM_DECL
     gnode_t             *env;               // shortcut to node where enum is declared
@@ -166,6 +167,7 @@ typedef struct {
     gtoken_t            storage;            // TOK_KEY_STATIC | TOK_KEY_EXTERN
     const char          *identifier;        // class name
     gnode_t             *superclass;        // super class ptr
+    bool                super_extern;       // flag set when a superclass is declared as extern
     gnode_r             *protocols;         // array of protocols (currently unused)
     gnode_r             *decls;             // class declarations list
     symboltable_t       *symtable;          // class internal symbol table
@@ -264,7 +266,7 @@ gnode_t *gnode_enum_decl_create (gtoken_s token, const char *identifier, gtoken_
 gnode_t *gnode_class_decl_create (gtoken_s token, const char *identifier, gtoken_t access_specifier, gtoken_t storage_specifier, gnode_t *superclass, gnode_r *protocols, gnode_r *declarations, bool is_struct, gnode_t *decl);
 gnode_t *gnode_module_decl_create (gtoken_s token, const char *identifier, gtoken_t access_specifier, gtoken_t storage_specifier, gnode_r *declarations, gnode_t *decl);
 gnode_t *gnode_variable_decl_create (gtoken_s token, gtoken_t type, gtoken_t access_specifier, gtoken_t storage_specifier, gnode_r *declarations, gnode_t *decl);
-gnode_t *gnode_variable_create (gtoken_s token, const char *identifier, const char *annotation_type, gtoken_t storage_specifier, gnode_t *expr, gnode_t *decl);
+gnode_t *gnode_variable_create (gtoken_s token, const char *identifier, const char *annotation_type, gnode_t *expr, gnode_t *decl, gnode_variable_decl_t *vdecl);
 
 gnode_t *gnode_function_decl_create (gtoken_s token, const char *identifier, gtoken_t access_specifier, gtoken_t storage_specifier, gnode_r *params, gnode_compound_stmt_t *block, gnode_t *decl);
 
@@ -289,6 +291,7 @@ gupvalue_t *gnode_function_add_upvalue(gnode_function_decl_t *f, gnode_var_t *sy
 cstring_r  *cstring_array_create (void);
 void_r  *void_array_create (void);
 void    gnode_array_sethead(gnode_r *list, gnode_t *node);
+gnode_t *gnode2class (gnode_t *node, bool *isextern);
 
 bool    gnode_is_equal (gnode_t *node1, gnode_t *node2);
 bool    gnode_is_expression (gnode_t *node);

+ 32 - 11
src/compiler/gravity_codegen.c

@@ -468,10 +468,13 @@ static void visit_loop_while_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
 
     uint32_t labelTrue  = ircode_newlabel(code);
     uint32_t labelFalse = ircode_newlabel(code);
+    uint32_t labelCheck = ircode_newlabel(code);
     ircode_setlabel_true(code, labelTrue);
     ircode_setlabel_false(code, labelFalse);
+    ircode_setlabel_check(code, labelCheck);
 
 	ircode_marklabel(code, labelTrue, LINE_NUMBER(node));
+    ircode_marklabel(code, labelCheck, LINE_NUMBER(node));
     visit(node->cond);
     uint32_t reg = ircode_register_pop(code);
     if (reg == REGISTER_ERROR) report_error(self, (gnode_t *)node, "Invalid while condition expression.");
@@ -484,6 +487,7 @@ static void visit_loop_while_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
 
     ircode_unsetlabel_true(code);
     ircode_unsetlabel_false(code);
+    ircode_unsetlabel_check(code);
 }
 
 static void visit_loop_repeat_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
@@ -499,10 +503,13 @@ static void visit_loop_repeat_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
 
     uint32_t labelTrue  = ircode_newlabel(code);
     uint32_t labelFalse = ircode_newlabel(code);    // end label is necessary to handle optional break statement
+    uint32_t labelCheck = ircode_newlabel(code);
     ircode_setlabel_true(code, labelTrue);
     ircode_setlabel_false(code, labelFalse);
+    ircode_setlabel_check(code, labelCheck);
 
 	ircode_marklabel(code, labelTrue, LINE_NUMBER(node));
+    ircode_marklabel(code, labelCheck, LINE_NUMBER(node));
     visit(node->stmt);
     visit(node->expr);
     uint32_t reg = ircode_register_pop(code);
@@ -514,6 +521,7 @@ static void visit_loop_repeat_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
 
     ircode_unsetlabel_true(code);
     ircode_unsetlabel_false(code);
+    ircode_unsetlabel_check(code);
 }
 
 static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
@@ -582,8 +590,10 @@ static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
     // while code
     uint32_t labelTrue  = ircode_newlabel(code);
     uint32_t labelFalse = ircode_newlabel(code);
+    uint32_t labelCheck = ircode_newlabel(code);
     ircode_setlabel_true(code, labelTrue);
     ircode_setlabel_false(code, labelFalse);
+    ircode_setlabel_check(code, labelCheck);
 
 	ircode_marklabel(code, labelTrue, LINE_NUMBER(node));
 	ircode_add(code, JUMPF, $value, labelFalse, 1, LINE_NUMBER(node));				// flag JUMPF instruction to check ONLY BOOL values
@@ -614,6 +624,7 @@ static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
     temp = ircode_register_pop(code);                                   // --TEMP => 4
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
 
+    ircode_marklabel(code, labelCheck, LINE_NUMBER(node));
     // update $value for the next check
     // $value = $expr.iterate($value);
     temp1 = ircode_register_push_temp(code);                            // ++TEMP => 5
@@ -636,6 +647,7 @@ static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
 
     ircode_unsetlabel_true(code);
     ircode_unsetlabel_false(code);
+    ircode_unsetlabel_check(code);
 
     temp = ircode_register_pop(code);                                   // --TEMP => 3
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
@@ -683,7 +695,7 @@ static void visit_jump_stmt (gvisitor_t *self, gnode_jump_stmt_t *node) {
         uint32_t label = ircode_getlabel_false(code);
 		ircode_add(code, JUMP, label, 0, 0, LINE_NUMBER(node)); // goto $end;
     } else if (type == TOK_KEY_CONTINUE) {
-        uint32_t label = ircode_getlabel_true(code);
+        uint32_t label = ircode_getlabel_check(code);
 		ircode_add(code, JUMP, label, 0, 0, LINE_NUMBER(node)); // goto $start;
     } else if (type == TOK_KEY_RETURN) {
         if (node->expr) {
@@ -1156,21 +1168,30 @@ static void visit_class_decl (gvisitor_t *self, gnode_class_decl_t *node) {
 
     // mark the class as a struct
     c->is_struct = node->is_struct;
-
+    
     // check if class has a declared superclass
     if (node->superclass) {
         // node->superclass should be a gnode_class_decl_t at this point
-        assert(NODE_ISA_CLASS(node->superclass));
-
+        if (NODE_ISA_CLASS(node->superclass)) {
+            node->super_extern = (((gnode_class_decl_t *)node->superclass)->storage == TOK_KEY_EXTERN);
+        } else {
+            node->superclass = gnode2class((gnode_t*)node->superclass, &node->super_extern);
+            if (!node->superclass) report_error(self, (gnode_t*)node, "Unable to set superclass to non class object.");
+        }
+        
         gnode_class_decl_t *super = (gnode_class_decl_t *)node->superclass;
-        if (super->data) {
-            // means that superclass has already been processed and its runtime representation is available
-            gravity_class_setsuper(c, (gravity_class_t *)super->data);
+        if (node->super_extern) {
+            gravity_class_setsuper_extern(c, super->identifier);
         } else {
-            // 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;
-            marray_push(gnode_class_decl_t *, data->superfix, node);
+            if (super->data) {
+                // means that superclass has already been processed and its runtime representation is available
+                gravity_class_setsuper(c, (gravity_class_t *)super->data);
+            } else {
+                // 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;
+                marray_push(gnode_class_decl_t *, data->superfix, node);
+            }
         }
     }
 

+ 17 - 0
src/compiler/gravity_ircode.c

@@ -19,6 +19,7 @@ struct ircode_t {
 
     uint32_r    label_true;                 // labels used in loops
     uint32_r    label_false;
+    uint32_r    label_check;
     uint32_t    label_counter;
 
     uint32_t    maxtemp;                    // maximum number of temp registers used in this ircode
@@ -46,6 +47,7 @@ ircode_t *ircode_create (uint16_t nlocals) {
     marray_init(*code->list);
     marray_init(code->label_true);
     marray_init(code->label_false);
+    marray_init(code->label_check);
     marray_init(code->registers);
     marray_init(code->context);
 
@@ -70,6 +72,7 @@ void ircode_free (ircode_t *code) {
     marray_destroy(code->registers);
     marray_destroy(code->label_true);
     marray_destroy(code->label_false);
+    marray_destroy(code->label_check);
     mem_free(code->list);
     mem_free(code);
 }
@@ -333,6 +336,10 @@ void ircode_setlabel_false (ircode_t *code, uint32_t nlabel) {
     marray_push(uint32_t, code->label_false, nlabel);
 }
 
+void ircode_setlabel_check (ircode_t *code, uint32_t nlabel) {
+    marray_push(uint32_t, code->label_check, nlabel);
+}
+
 void ircode_unsetlabel_true (ircode_t *code) {
     marray_pop(code->label_true);
 }
@@ -341,6 +348,10 @@ void ircode_unsetlabel_false (ircode_t *code) {
     marray_pop(code->label_false);
 }
 
+void ircode_unsetlabel_check (ircode_t *code) {
+    marray_pop(code->label_check);
+}
+
 uint32_t ircode_getlabel_true (ircode_t *code) {
     size_t n = marray_size(code->label_true);
     uint32_t v = marray_get(code->label_true, n-1);
@@ -353,6 +364,12 @@ uint32_t ircode_getlabel_false (ircode_t *code) {
     return v;
 }
 
+uint32_t ircode_getlabel_check (ircode_t *code) {
+    size_t n = marray_size(code->label_check);
+    uint32_t v = marray_get(code->label_check, n-1);
+    return v;
+}
+
 void ircode_marklabel (ircode_t *code, uint32_t nlabel, uint32_t lineno) {
 	inst_t *inst = inst_new(0, nlabel, 0, 0, LABEL_TAG, 0, 0.0, lineno);
     marray_push(inst_t*, *code->list, inst);

+ 3 - 0
src/compiler/gravity_ircode.h

@@ -69,10 +69,13 @@ void        ircode_patch_init (ircode_t *code, uint16_t index);
 uint32_t    ircode_newlabel (ircode_t *code);
 void        ircode_setlabel_true (ircode_t *code, uint32_t nlabel);
 void        ircode_setlabel_false (ircode_t *code, uint32_t nlabel);
+void        ircode_setlabel_check (ircode_t *code, uint32_t nlabel);
 void        ircode_unsetlabel_true (ircode_t *code);
 void        ircode_unsetlabel_false (ircode_t *code);
+void        ircode_unsetlabel_check (ircode_t *code);
 uint32_t    ircode_getlabel_true (ircode_t *code);
 uint32_t    ircode_getlabel_false (ircode_t *code);
+uint32_t    ircode_getlabel_check (ircode_t *code);
 void		ircode_marklabel (ircode_t *code, uint32_t nlabel, uint32_t lineno);
 
 void        inst_setskip (inst_t *inst);

+ 24 - 20
src/compiler/gravity_parser.c

@@ -325,11 +325,14 @@ static char *cstring_from_token (gravity_parser_t *parser, gtoken_s token) {
     return str;
 }
 
-static gnode_t *local_store_declaration (gravity_parser_t *parser, const char *identifier, gtoken_t access_specifier, gtoken_t storage_specifier, gnode_t *declaration) {
+static gnode_t *local_store_declaration (gravity_parser_t *parser, const char *identifier, const char *annotation_type, gtoken_t access_specifier, gtoken_t storage_specifier, gnode_t *declaration) {
     gnode_r *decls = gnode_array_create();
-    gnode_t *decl = gnode_variable_create(declaration->token, identifier ? string_dup(identifier) : NULL, NULL, access_specifier, declaration, LAST_DECLARATION());
+    
+    gnode_variable_decl_t *vdecl = (gnode_variable_decl_t *)gnode_variable_decl_create(declaration->token, TOK_KEY_VAR, access_specifier, storage_specifier, decls, LAST_DECLARATION());
+    gnode_t *decl = gnode_variable_create(declaration->token, identifier ? string_dup(identifier) : NULL, annotation_type, declaration, LAST_DECLARATION(), vdecl);
     gnode_array_push(decls, decl);
-    return gnode_variable_decl_create(declaration->token, TOK_KEY_VAR, access_specifier, storage_specifier, decls, LAST_DECLARATION());
+    
+    return (gnode_t *)vdecl;
 }
 
 static gnode_t *decl_check_access_specifier (gnode_t *node) {
@@ -1251,7 +1254,7 @@ static gnode_t *parse_getter_setter (gravity_parser_t *parser) {
         if (strcmp(identifier, GETTER_FUNCTION_NAME) == 0) {
             is_getter = true;
             params = gnode_array_create();    // add implicit SELF param
-            gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SELF_PARAMETER_NAME), NULL, 0, NULL, LAST_DECLARATION()));
+            gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SELF_PARAMETER_NAME), NULL, NULL, LAST_DECLARATION(), NULL));
         }
 
         // setter case: could have explicit parameters (otherwise value is implicit)
@@ -1264,8 +1267,8 @@ static gnode_t *parse_getter_setter (gravity_parser_t *parser) {
                 parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
             } else {
                 params = gnode_array_create();    // add implicit SELF and VALUE params
-                gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SELF_PARAMETER_NAME), NULL, 0, NULL, LAST_DECLARATION()));
-                gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SETTER_PARAMETER_NAME), NULL, 0, NULL, LAST_DECLARATION()));
+                gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SELF_PARAMETER_NAME), NULL, NULL, LAST_DECLARATION(), NULL));
+                gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SETTER_PARAMETER_NAME), NULL, NULL, LAST_DECLARATION(), NULL));
             }
         }
         mem_free(identifier);
@@ -1363,7 +1366,7 @@ loop:
     // 1. CONST must be followed by an assignment expression ?
     // 2. check if identifier is unique inside variable declarations
 
-    decl = gnode_variable_create(token2, identifier, type_annotation, access_specifier, expr, LAST_DECLARATION());
+    decl = gnode_variable_create(token2, identifier, type_annotation, expr, LAST_DECLARATION(), node);
     if (decl) {
         ((gnode_var_t *)decl)->iscomputed = is_computed;
         gnode_array_push(decls, decl);
@@ -1558,7 +1561,7 @@ static gnode_t *parse_enum_declaration (gravity_parser_t *parser, gtoken_t acces
         REPORT_ERROR(token, "Empty enum %s not allowed.", identifier);
     }
 
-    if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(parser, identifier, access_specifier, storage_specifier, (gnode_t *)node);
+    if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(parser, identifier, NULL, access_specifier, storage_specifier, (gnode_t *)node);
     return (gnode_t *)node;
 }
 
@@ -1640,7 +1643,7 @@ static gnode_t *parse_function_declaration (gravity_parser_t *parser, gtoken_t a
     if (IS_FUNCTION_ENCLOSED()) {
         gnode_function_decl_t *func = (gnode_function_decl_t *)node;
         func->is_closure = true;
-        return local_store_declaration(parser, func->identifier, access_specifier, storage_specifier, node);
+        return local_store_declaration(parser, func->identifier, NULL, access_specifier, storage_specifier, node);
     }
     return node;
 }
@@ -1737,11 +1740,11 @@ static gnode_t *parse_class_declaration (gravity_parser_t *parser, gtoken_t acce
     if (storage_specifier != TOK_KEY_EXTERN) parse_required(parser, TOK_OP_OPEN_CURLYBRACE);
     gnode_r *declarations = gnode_array_create();
 
-    // if class is declared inside another class then a hidden implicit privare "outer" instance var at index 0
+    // if class is declared inside another class then a hidden implicit private "outer" instance var at index 0
     // is automatically added
     if (IS_CLASS_ENCLOSED()) {
         gnode_r *decls = gnode_array_create();
-        gnode_t *outer_var = gnode_variable_create(NO_TOKEN, string_dup(OUTER_IVAR_NAME), NULL, 0, NULL, LAST_DECLARATION());
+        gnode_t *outer_var = gnode_variable_create(NO_TOKEN, string_dup(OUTER_IVAR_NAME), NULL, NULL, LAST_DECLARATION(), NULL);
         gnode_array_push(decls, outer_var);
 
         gnode_t *outer_decl = gnode_variable_decl_create(NO_TOKEN, TOK_KEY_VAR, TOK_KEY_PRIVATE, 0, decls, LAST_DECLARATION());
@@ -1771,7 +1774,8 @@ static gnode_t *parse_class_declaration (gravity_parser_t *parser, gtoken_t acce
     // finish setup node class
     node->decls = declarations;
 
-    if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(parser, identifier, access_specifier, storage_specifier, (gnode_t *)node);
+    const char *class_manifest_type = gravity_class_class->identifier;
+    if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(parser, identifier, string_dup(class_manifest_type), access_specifier, storage_specifier, (gnode_t *)node);
     return (gnode_t *)node;
 }
 
@@ -1780,10 +1784,10 @@ static gnode_r *parse_optional_parameter_declaration (gravity_parser_t *parser,
     DECLARE_LEXER;
 
     gtoken_s    token = NO_TOKEN;
-    gnode_t        *node = NULL;
+    gnode_t     *node = NULL;
     gnode_t     *default_value = NULL;
-    const char    *identifier = NULL;
-    const char    *type_annotation = NULL;
+    const char  *identifier = NULL;
+    const char  *type_annotation = NULL;
 
     // (IDENTIFIER type_annotation?) (',' type_annotation)*
     // type_annotation: ':' identifier
@@ -1799,7 +1803,7 @@ static gnode_r *parse_optional_parameter_declaration (gravity_parser_t *parser,
     // ALWAYS add an implicit SELF parameter
     // string_dup mandatory here because when the node will be freed
     // memory for the identifier will be deallocated
-    node = gnode_variable_create(token, string_dup(SELF_PARAMETER_NAME), type_annotation, 0, NULL, LAST_DECLARATION());
+    node = gnode_variable_create(token, string_dup(SELF_PARAMETER_NAME), type_annotation, NULL, LAST_DECLARATION(), NULL);
     DEBUG_PARSER("PARAMETER: %s %s", SELF_PARAMETER_NAME, (type_annotation) ? type_annotation : "");
     if (node) gnode_array_push(params, node);
     if (is_implicit) return params;
@@ -1828,7 +1832,7 @@ loop:
     if (default_value && has_default_values) *has_default_values = true;
     
     // fill parameters array with the new node
-    node = gnode_variable_create(token, identifier, type_annotation, 0, default_value, LAST_DECLARATION());
+    node = gnode_variable_create(token, identifier, type_annotation, default_value, LAST_DECLARATION(), NULL);
     if (node) gnode_array_push(params, node);
     DEBUG_PARSER("PARAMETER: %s %s", identifier, (type_annotation) ? type_annotation : "");
 
@@ -2488,7 +2492,7 @@ static void parser_register_core_classes (gravity_parser_t *parser) {
     uint32_t i = 0;
     while (list[i]) {
         const char *identifier = list[i];
-        gnode_t *node = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, 0, NULL, LAST_DECLARATION());
+        gnode_t *node = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, NULL, LAST_DECLARATION(), NULL);
         gnode_array_push(decls, node);
         ++i;
     }
@@ -2503,7 +2507,7 @@ static void parser_register_optional_classes (gravity_parser_t *parser) {
     gnode_r    *decls = gnode_array_create();
 
     #ifdef GRAVITY_INCLUDE_MATH
-    gnode_t *decl = gnode_variable_create(NO_TOKEN, string_dup(GRAVITY_MATH_NAME()), NULL, 0, NULL, LAST_DECLARATION());
+    gnode_t *decl = gnode_variable_create(NO_TOKEN, string_dup(GRAVITY_MATH_NAME()), NULL, NULL, LAST_DECLARATION(), NULL);
     gnode_array_push(decls, decl);
     #endif
     
@@ -2518,7 +2522,7 @@ static void parser_register_optional_classes (gravity_parser_t *parser) {
         uint32_t i = 0;
         while (list[i]) {
             const char *identifier = list[i];
-            gnode_t *decl_node = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, 0, NULL, LAST_DECLARATION());
+            gnode_t *decl_node = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, NULL, LAST_DECLARATION(), NULL);
             gnode_array_push(decls, decl_node);
             ++i;
         }

+ 2 - 2
src/compiler/gravity_semacheck2.c

@@ -863,7 +863,7 @@ static void visit_class_decl (gvisitor_t *self, gnode_class_decl_t *node) {
         REPORT_ERROR(node, "%s is a special name and cannot be used as class identifier.", CLASS_CONSTRUCTOR_NAME);
         return;
     }
-
+    
     // check superclass
     if (node->superclass) {
         // get super class identifier and reset the field (so in case of error it cannot be accessed)
@@ -883,7 +883,7 @@ static void visit_class_decl (gvisitor_t *self, gnode_class_decl_t *node) {
         if (!target) {
             REPORT_ERROR(id, "Unable to find superclass %s for class %s.", id->value, node->identifier);
         } else {
-            gnode_class_decl_t *target_class = NODE_ISA(target, NODE_CLASS_DECL) ? (gnode_class_decl_t *)target : NULL;
+            gnode_class_decl_t *target_class = (gnode_class_decl_t *)gnode2class(target, &node->super_extern);
             if (!target_class) {
                 REPORT_ERROR(id, "Unable to set non class %s as superclass of %s.", id->value, node->identifier);
             } else if ((gnode_class_decl_t *)node == (gnode_class_decl_t *)target_class->superclass) {

+ 6 - 1
src/runtime/gravity_core.c

@@ -1523,6 +1523,12 @@ static bool class_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     // perform alloc (then check for init)
     gravity_gc_setenabled(vm, false);
     gravity_instance_t *instance = gravity_instance_new(vm, c);
+    
+    // special case: check if superclass is an xdata class
+    gravity_delegate_t *delegate = gravity_vm_delegate(vm);
+    if (c->superclass->xdata && delegate->bridge_initinstance) {
+        delegate->bridge_initinstance(vm, c->superclass->xdata, VALUE_FROM_NULL, instance, NULL, 1);
+    }
 
     // if is inner class then ivar 0 is reserved for a reference to its outer class
     if (c->has_outer) gravity_instance_setivar(instance, 0, gravity_vm_getslot(vm, 0));
@@ -1540,7 +1546,6 @@ static bool class_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     }
 
     // no closure found (means no constructor found in this class)
-    gravity_delegate_t *delegate = gravity_vm_delegate(vm);
     if (c->xdata && delegate->bridge_initinstance) {
         // even if no closure is found try to execute the default bridge init instance (if class is bridged)
         if (nargs != 1) RETURN_ERROR("No init with %d parameters found in class %s", nargs-1, c->identifier);

+ 13 - 4
src/shared/gravity_value.c

@@ -173,6 +173,11 @@ bool gravity_class_setsuper (gravity_class_t *baseclass, gravity_class_t *superc
     return true;
 }
 
+bool gravity_class_setsuper_extern (gravity_class_t *baseclass, const char *identifier) {
+    if (identifier) baseclass->superlook = string_dup(identifier);
+    return true;
+}
+
 gravity_class_t *gravity_class_new_single (gravity_vm *vm, const char *identifier, uint32_t nivar) {
     gravity_class_t *c = (gravity_class_t *)mem_alloc(NULL, sizeof(gravity_class_t));
     assert(c);
@@ -248,12 +253,14 @@ void gravity_class_setxdata (gravity_class_t *c, void *xdata) {
 
 void gravity_class_serialize (gravity_class_t *c, json_t *json) {
     json_begin_object(json, c->identifier);
-    json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_CLASS);            // MANDATORY 1st FIELD
-    json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, c->identifier);        // MANDATORY 2nd FIELD
-
+    json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_CLASS);     // MANDATORY 1st FIELD
+    json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, c->identifier);    // MANDATORY 2nd FIELD
+    
     // avoid write superclass name if it is the default Object one
     if ((c->superclass) && (c->superclass->identifier) && (strcmp(c->superclass->identifier, GRAVITY_CLASS_OBJECT_NAME) != 0)) {
         json_add_cstring(json, GRAVITY_JSON_LABELSUPER, c->superclass->identifier);
+    } else if (c->superlook) {
+        json_add_cstring(json, GRAVITY_JSON_LABELSUPER, c->superlook);
     }
 
     // get c meta class
@@ -391,6 +398,8 @@ static void gravity_class_free_internal (gravity_vm *vm, gravity_class_t *c, boo
     }
 
     if (c->identifier) mem_free((void *)c->identifier);
+    if (c->superlook) mem_free((void *)c->superlook);
+    
     if (!skip_base) {
         // base classes have functions not registered inside VM so manually free all of them
         gravity_hash_iterate(c->htable, gravity_hash_finteralfree, NULL);
@@ -1579,7 +1588,7 @@ gravity_instance_t *gravity_instance_new (gravity_vm *vm, gravity_class_t *c) {
     
     if (c->nivars) instance->ivars = (gravity_value_t *)mem_alloc(NULL, c->nivars * sizeof(gravity_value_t));
     for (uint32_t i=0; i<c->nivars; ++i) instance->ivars[i] = VALUE_FROM_NULL;
-
+    
     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) instance);
     return instance;
 }

+ 4 - 2
src/shared/gravity_value.h

@@ -66,8 +66,8 @@
 extern "C" {
 #endif
 
-#define GRAVITY_VERSION						"0.6.7"     // git tag 0.6.7
-#define GRAVITY_VERSION_NUMBER				0x000607    // git push --tags
+#define GRAVITY_VERSION						"0.6.9"     // git tag 0.6.9
+#define GRAVITY_VERSION_NUMBER				0x000609    // git push --tags
 #define GRAVITY_BUILD_DATE                  __DATE__
 
 #ifndef GRAVITY_ENABLE_DOUBLE
@@ -370,6 +370,7 @@ typedef struct gravity_class_s {
     bool                    unused;         // unused padding byte
     void                    *xdata;         // extra bridged data
     struct gravity_class_s  *superclass;    // reference to the super class
+    const char              *superlook;     // when a superclass is set to extern a runtime lookup must be performed
     gravity_hash_t          *htable;        // hash table
     uint32_t                nivars;         // number of instance variables
 	//gravity_value_r			inames;			    // ivar names
@@ -456,6 +457,7 @@ GRAVITY_API void                gravity_class_bind (gravity_class_t *c, const ch
 GRAVITY_API gravity_class_t     *gravity_class_getsuper (gravity_class_t *c);
 GRAVITY_API bool                gravity_class_grow (gravity_class_t *c, uint32_t n);
 GRAVITY_API bool                gravity_class_setsuper (gravity_class_t *subclass, gravity_class_t *superclass);
+GRAVITY_API bool                gravity_class_setsuper_extern (gravity_class_t *baseclass, const char *identifier);
 GRAVITY_API gravity_class_t     *gravity_class_new_single (gravity_vm *vm, const char *identifier, uint32_t nfields);
 GRAVITY_API gravity_class_t     *gravity_class_new_pair (gravity_vm *vm, const char *identifier, gravity_class_t *superclass, uint32_t nivar, uint32_t nsvar);
 GRAVITY_API gravity_class_t     *gravity_class_get_meta (gravity_class_t *c);

+ 25 - 0
test/unittest/break_continue.gravity

@@ -0,0 +1,25 @@
+#unittest {
+    name: "Break, continue in for loop.";
+    result: 8;
+};
+
+func main() {
+    var sum = 0;
+    var test:List = [1, 2, 3, 4];
+    
+    for (var item in test) {
+        if ((item == 1) || (item == 4)) {
+            continue;
+        }
+        sum += item;
+    }
+    
+    for (var item in test) {
+        if (item == 3) {
+            break;
+        }
+        sum += item;
+    }
+    
+    return sum;
+}

+ 16 - 0
test/unittest/local_class.gravity

@@ -0,0 +1,16 @@
+#unittest {
+    name: "Superclass set to local class declaration.";
+    result: 3;
+};
+
+func main() {
+    class C1b {
+        var v1 = 3;
+    }
+    
+    class C2 : C1b {
+    }
+    
+    var i1 = C2()
+    return i1.v1;
+}