Vladislav Folts 10 vuotta sitten
vanhempi
commit
61579a3891

BIN
bin/compiled.zip


+ 3 - 3
doc/wiki/eberon-record-constructor.md

@@ -71,7 +71,6 @@ Constructor 'Derived' has no parameters and 'Base' does have. To pass actual par
 
 Similar thing is for fields having parameterized constructors:
 
-
     Field = RECORD 
         PROCEDURE Field(a: INTEGER);
     END;
@@ -97,7 +96,8 @@ Fields of non-record types also can be referenced in initialization list. In thi
     PROCEDURE T.T() | f1(123), f2(TRUE);
     END;
 
-Fields must be referenced in the initializtion list in the same order as they declared in record. For example above "f2(TRUE), f1(123)" will cause compiler error. Fields of non-record types may be missed in the list - in this case they will be initialized using default values. Record fields having constructor without parameters will be initialized automatically and must be not referenced in initialzation list. All fields are initialized strictly in the same order as they declared in record - no matter if they referenced in the initialization list or initialized automatically. It is possible to use *SELF* in initialization list but be aware of the initialization order - do not use *SELF* to access not initialized field (the compiler does not diagnose that).
+Fields must be referenced in the initialization list in the same order as they declared in record. For example above "f2(TRUE), f1(123)" will cause compiler error.
+Only record's own fields can be referenced (not fields from base record). Fields of non-record types may be missed in the list - in this case they will be initialized using default values. Record fields having constructor without parameters will be initialized automatically and must be not referenced in initialzation list. All fields are initialized strictly in the same order as they declared in record - no matter if they referenced in the initialization list or initialized automatically. It is possible to use *SELF* in initialization list but be aware of the initialization order - do not use *SELF* to access not initialized field (the compiler does not diagnose that).
 
 The order of execution in constructor: 
 * call base constructor (if present), using *SUPER* (with parameters) or automatically (no parameters)
@@ -152,4 +152,4 @@ Constructor can be exported using '*' notation
     END;
 
 If constructor is exported then the record itself must be exported too.
-If constructor is not exported then record cannot be instantiated in other modules.
+If constructor is not exported then record cannot be instantiated or extended in other modules.

+ 20 - 1
src/eberon/EberonRecord.ob

@@ -1,6 +1,6 @@
 MODULE EberonRecord;
 IMPORT 
-    Cast, Context, EberonContext, EberonTypes, Errors, JS, JsMap, Scope, ScopeBase, Object, Stream, String, Types;
+    Cast, Context, EberonContext, EberonTypes, Errors, JS, JsMap, Object, Scope, ScopeBase, Stream, String, Types;
 CONST
     instantiateForVar* = 0;    
     instantiateForNew* = 1;    
@@ -446,6 +446,23 @@ BEGIN
     END;
 END;
 
+PROCEDURE checkIfFieldsInited(r: Record);
+VAR
+    fieldsWereNotInited: ARRAY * OF STRING;
+BEGIN
+    FOR i <- 0 TO LEN(r.customInitedfields) - 1 DO
+        f <- r.customInitedfields[i];
+        IF ~JsMap.hasString(r.fieldsInit, f) THEN
+            fieldsWereNotInited.add(f);
+        END;
+    END;
+
+    IF LEN(fieldsWereNotInited) # 0 THEN
+        Errors.raise("constructor '" + r.name + "' must initialize fields: " 
+                   + String.join(fieldsWereNotInited, ", "));
+    END;
+END;
+
 PROCEDURE Record.finalize();
 BEGIN
     SELF.finalized := TRUE;
@@ -470,6 +487,8 @@ BEGIN
     END;
     SELF.nonExportedMethods.clear();
 
+    checkIfFieldsInited(SELF);
+
     SUPER();
 END;
 

+ 0 - 9
src/eberon/eberon_context.js

@@ -705,15 +705,6 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
                 if (this.__baseConstructorWasCalled && (!baseConstructor || !baseConstructor.args().length))
                     throw new Errors.Error("base record constructor has no parameters and will be called automatically (do not use '| SUPER' to call base constructor)");
                 
-                var fieldsWereNotInited = [];
-                for(var i = 0; i < this.__boundType.customInitedfields.length; ++i){
-                    var f = this.__boundType.customInitedfields[i];
-                    if (this.__initedFields.indexOf(f) == -1)
-                        fieldsWereNotInited.push(f);
-                }
-                if (fieldsWereNotInited.length)
-                    throw new Errors.Error("constructor '" + Type.typeName(this.__boundType) + "' must initialize fields: " + fieldsWereNotInited);
-
                 this.codeGenerator().write(this.__boundType.inheritanceCode);
             }
             else

+ 9 - 1
src/ob/JsMap.ob

@@ -30,7 +30,15 @@ VAR
 BEGIN
     JS.do("result = m.hasOwnProperty(s)");
     RETURN result
-END has;
+END;
+
+PROCEDURE hasString*(m: Strings; s: STRING): BOOLEAN;
+VAR
+    result: BOOLEAN;
+BEGIN
+    JS.do("result = m.hasOwnProperty(s)");
+    RETURN result
+END;
 
 PROCEDURE find*(m: Type; s: STRING; VAR r: Object.PType): BOOLEAN;
 VAR

+ 8 - 0
test/test_unit_eberon.js

@@ -1223,6 +1223,14 @@ exports.suite = {
              ["PROCEDURE T.T() | f1(1), f3(3), f2(2); END;", "field 'f2' must be initialized before 'f3'"]
             )
         ),
+    "fields with constructor but record without constructor": testWithContext(
+        context(grammar.declarationSequence,
+                "TYPE Field = RECORD PROCEDURE Field(a: INTEGER); END;"
+              + "PROCEDURE Field.Field(a: INTEGER); END;"
+              ),
+        pass(),
+        fail(["TYPE T = RECORD f: Field; END;", "constructor 'T' must initialize fields: f"])
+        ),
     "inherit constructor parameters": testWithContext(
         context(grammar.expression,
                 "TYPE Base = RECORD PROCEDURE Base(i: INTEGER); END;"