瀏覽代碼

more tests

Vladislav Folts 10 年之前
父節點
當前提交
61579a3891
共有 6 個文件被更改,包括 40 次插入14 次删除
  1. 二進制
      bin/compiled.zip
  2. 3 3
      doc/wiki/eberon-record-constructor.md
  3. 20 1
      src/eberon/EberonRecord.ob
  4. 0 9
      src/eberon/eberon_context.js
  5. 9 1
      src/ob/JsMap.ob
  6. 8 0
      test/test_unit_eberon.js

二進制
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:
 Similar thing is for fields having parameterized constructors:
 
 
-
     Field = RECORD 
     Field = RECORD 
         PROCEDURE Field(a: INTEGER);
         PROCEDURE Field(a: INTEGER);
     END;
     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);
     PROCEDURE T.T() | f1(123), f2(TRUE);
     END;
     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: 
 The order of execution in constructor: 
 * call base constructor (if present), using *SUPER* (with parameters) or automatically (no parameters)
 * call base constructor (if present), using *SUPER* (with parameters) or automatically (no parameters)
@@ -152,4 +152,4 @@ Constructor can be exported using '*' notation
     END;
     END;
 
 
 If constructor is exported then the record itself must be exported too.
 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;
 MODULE EberonRecord;
 IMPORT 
 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
 CONST
     instantiateForVar* = 0;    
     instantiateForVar* = 0;    
     instantiateForNew* = 1;    
     instantiateForNew* = 1;    
@@ -446,6 +446,23 @@ BEGIN
     END;
     END;
 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();
 PROCEDURE Record.finalize();
 BEGIN
 BEGIN
     SELF.finalized := TRUE;
     SELF.finalized := TRUE;
@@ -470,6 +487,8 @@ BEGIN
     END;
     END;
     SELF.nonExportedMethods.clear();
     SELF.nonExportedMethods.clear();
 
 
+    checkIfFieldsInited(SELF);
+
     SUPER();
     SUPER();
 END;
 END;
 
 

+ 0 - 9
src/eberon/eberon_context.js

@@ -705,15 +705,6 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
                 if (this.__baseConstructorWasCalled && (!baseConstructor || !baseConstructor.args().length))
                 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)");
                     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);
                 this.codeGenerator().write(this.__boundType.inheritanceCode);
             }
             }
             else
             else

+ 9 - 1
src/ob/JsMap.ob

@@ -30,7 +30,15 @@ VAR
 BEGIN
 BEGIN
     JS.do("result = m.hasOwnProperty(s)");
     JS.do("result = m.hasOwnProperty(s)");
     RETURN result
     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;
 PROCEDURE find*(m: Type; s: STRING; VAR r: Object.PType): BOOLEAN;
 VAR
 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'"]
              ["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(
     "inherit constructor parameters": testWithContext(
         context(grammar.expression,
         context(grammar.expression,
                 "TYPE Base = RECORD PROCEDURE Base(i: INTEGER); END;"
                 "TYPE Base = RECORD PROCEDURE Base(i: INTEGER); END;"