2
0
Эх сурвалжийг харах

call parameterized base constructor

Vladislav Folts 10 жил өмнө
parent
commit
a7b81d926b

BIN
bin/compiled.zip


+ 9 - 5
src/context.js

@@ -1759,18 +1759,22 @@ exports.RecordDecl = ChainedContext.extend({
         var gen = CodeGenerator.makeGenerator();
         gen.write("function " + this.__cons + "()");
         gen.openScope();
-        gen.write(this._generateFieldsInitializationCode());
+        gen.write(this._generateBaseConstructorCallCode() 
+                + this._generateFieldsInitializationCode());
         gen.closeScope("");
         return gen.result();
     },
-    _generateFieldsInitializationCode: function(){
-        var type = this.__type;
-        var baseType = Type.recordBase(type);
+    _generateBaseConstructorCallCode: function(){
+        var baseType = Type.recordBase(this.__type);
         var qualifiedBase = baseType ? this.qualifyScope(Type.recordScope(baseType)) + Type.typeName(baseType) : undefined; 
         var result = "";
         if (baseType)
             result += qualifiedBase + ".call(this);\n";
-        var ownFields = Type.recordOwnFields(type);
+        return result;
+    },
+    _generateFieldsInitializationCode: function(){
+        var result = "";
+        var ownFields = Type.recordOwnFields(this.__type);
         for(var f in ownFields){
             var fieldType = ownFields[f].type();
             result += "this." + mangleField(f, fieldType) + " = " + fieldType.initializer(this, false, "") + ";\n";

+ 46 - 9
src/eberon/EberonConstructor.ob

@@ -1,25 +1,40 @@
 MODULE EberonConstructor;
-IMPORT Code, EberonRecord, LanguageContext, Procedure, Types;
+IMPORT Code, EberonRecord, LanguageContext, Procedure, Stream, Types;
 TYPE
     ConstructorCall = RECORD(Procedure.StdCall)
         recordType: EberonRecord.PRecord;
     END;
+    PConstructorCall = POINTER TO ConstructorCall;
 
-PROCEDURE ConstructorCall.make(args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): Code.PExpression;
+    BaseConstructorCall = RECORD(ConstructorCall)
+    END;
+
+PROCEDURE checkArgs(call: ConstructorCall; args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): STRING;
 BEGIN
     argCode <- Procedure.makeArgumentsCode(cx);
-    Procedure.processArguments(args, SELF.args, argCode, cx.types);
-    RETURN Code.makeSimpleExpression(SELF.recordType.initializer(cx^, FALSE, argCode.result()), SELF.recordType);
+    Procedure.processArguments(args, call.args, argCode, cx.types);
+    RETURN argCode.result();
 END;
 
-PROCEDURE makeConstructorCall*(
+PROCEDURE ConstructorCall.make(args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): Code.PExpression;
+BEGIN
+    argCode <- checkArgs(SELF, args, cx);
+    RETURN Code.makeSimpleExpression(SELF.recordType.initializer(cx^, FALSE, argCode), SELF.recordType);
+END;
+
+PROCEDURE BaseConstructorCall.make(args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): Code.PExpression;
+BEGIN
+    argCode <- checkArgs(SELF, args, cx);
+    code <- cx.qualifyScope(SELF.recordType.scope) + SELF.recordType.cons + ".call(this, " + argCode + ");" + Stream.kCR;
+    RETURN Code.makeSimpleExpression(code, NIL);
+END;
+
+PROCEDURE makeCallGenerator(
     type: EberonRecord.PRecord; 
-    cx: LanguageContext.PType
+    cx: LanguageContext.PType;
+    call: PConstructorCall
     ): Procedure.PCallGenerator;
-VAR
-    call: POINTER TO ConstructorCall;
 BEGIN
-    NEW(call);
     Procedure.initStdCall(call);
     call.recordType := type; 
     IF type.customConstructor # NIL THEN
@@ -28,4 +43,26 @@ BEGIN
     RETURN Procedure.makeCallGenerator(call, cx)
 END;
 
+PROCEDURE makeConstructorCall*(
+    type: EberonRecord.PRecord; 
+    cx: LanguageContext.PType
+    ): Procedure.PCallGenerator;
+VAR
+    call: PConstructorCall;
+BEGIN
+    NEW(call);
+    RETURN makeCallGenerator(type, cx, call);
+END;
+
+PROCEDURE makeBaseConstructorCall*(
+    type: EberonRecord.PRecord; 
+    cx: LanguageContext.PType
+    ): Procedure.PCallGenerator;
+VAR
+    call: POINTER TO BaseConstructorCall;
+BEGIN
+    NEW(call);
+    RETURN makeCallGenerator(type, cx, call);
+END;
+
 END EberonConstructor.

+ 10 - 3
src/eberon/EberonRecord.ob

@@ -7,7 +7,8 @@ TYPE
         PROCEDURE addMethod(methodId: Context.PIdentdefInfo; type: Types.PProcedure);
         PROCEDURE defineMethod(methodId: Context.PIdentdefInfo; type: EberonTypes.PMethodType);
         PROCEDURE requireNewOnly();
-        PROCEDURE setRecordInitializationCode(fieldsInitializationCode, inheritanceCode: STRING);
+        PROCEDURE setBaseConstructorCallCode(code: STRING);
+        PROCEDURE setRecordInitializationCode(baseConstructorCallCode, fieldsInitializationCode, inheritanceCode: STRING);
 
         customConstructor-: Types.PDefinedProcedure;
         finalized: BOOLEAN;
@@ -19,7 +20,7 @@ TYPE
         declaredAsVariable: BOOLEAN;
         lazyDefinitions: JsMap.Type;
         nonExportedMethods: ARRAY * OF STRING;
-        inheritanceCode, fieldsInitializationCode-: STRING;
+        inheritanceCode, baseConstructorCallCode-, fieldsInitializationCode-: STRING;
     END;
     PRecord* = POINTER TO Record;
 
@@ -297,8 +298,14 @@ BEGIN
     SELF.createByNewOnly := TRUE;
 END;
 
-PROCEDURE Record.setRecordInitializationCode(fieldsInitializationCode, inheritanceCode: STRING);
+PROCEDURE Record.setBaseConstructorCallCode(code: STRING);
 BEGIN
+    SELF.baseConstructorCallCode := code;
+END;
+
+PROCEDURE Record.setRecordInitializationCode(baseConstructorCallCode, fieldsInitializationCode, inheritanceCode: STRING);
+BEGIN
+    SELF.baseConstructorCallCode := baseConstructorCallCode;
     SELF.fieldsInitializationCode := fieldsInitializationCode;
     SELF.inheritanceCode := inheritanceCode;
 END;

+ 62 - 8
src/eberon/eberon_context.js

@@ -183,14 +183,14 @@ var Identdef = Context.Identdef.extend({
     }
 });
 
-function makeConstructorCall(context, type){
+function makeConstructorCall(context, type, call){
     var l = context.language();
     var cx = {
         types: l.types, 
         rtl: l.rtl, 
         qualifyScope: context.qualifyScope.bind(context)
         };
-    return EberonConstructor.makeConstructorCall(type, cx);
+    return call(type, cx);
     }
 
 var Designator = Context.Designator.extend({
@@ -252,7 +252,7 @@ var Designator = Context.Designator.extend({
     __beginCall: function(){
         var type = this._currentType();
         if (type instanceof Type.TypeId && type.type() instanceof Type.Record){
-            this.__procCall = makeConstructorCall(this, type.type());
+            this.__procCall = makeConstructorCall(this, type.type(), EberonConstructor.makeConstructorCall);
             this._discardCode();
         }
         else
@@ -442,6 +442,7 @@ var RecordDecl = Context.RecordDecl.extend({
         var type = this.type();
         var inheritanceCode = this._generateInheritance();
         type.setRecordInitializationCode(
+            this._generateBaseConstructorCallCode(),
             this._generateFieldsInitializationCode(), 
             inheritanceCode);
         this.currentScope().addFinalizer(function(){
@@ -470,6 +471,37 @@ function handleTypePromotionMadeInSeparateStatement(msg){
     return false;
 }
 
+function getConstructorSuperMsg(){}
+
+var BaseInit = Context.Chained.extend({
+    init: function EberonContext$BaseInit(parent){
+        Context.Chained.prototype.init.call(this, parent);
+        this.__type = undefined;
+        this.__baseConstructorCall = undefined;
+    },
+    codeGenerator: function(){return CodeGenerator.nullGenerator();},
+    handleMessage: function(msg){
+        if (msg == Context.beginCallMsg)
+            return;
+        if (msg == Context.endCallMsg){
+            var e = this.__baseConstructorCall.end();
+            this.__type.setBaseConstructorCallCode(e.code());
+            return;
+        }
+        return Context.Chained.prototype.handleMessage.call(this, msg);
+    },
+    handleExpression: function(e){
+        this.__baseConstructorCall.handleArgument(e);
+    },
+    handleLiteral: function(s){
+        if (s == "SUPER"){
+            var ms = this.handleMessage(getConstructorSuperMsg);
+            this.__type = ms.type;
+            this.__baseConstructorCall = makeConstructorCall(this, Type.recordBase(this.__type), EberonConstructor.makeBaseConstructorCall);
+        }
+    }
+});
+
 var ProcOrMethodDecl = Context.ProcDecl.extend({
     init: function EberonContext$ProcOrMethodDecl(parent, stdSymbols){
         Context.ProcDecl.prototype.init.call(this, parent, stdSymbols);
@@ -478,6 +510,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
         this.__boundType = undefined;
         this.__endingId = undefined;
         this.__isConstructor = false;
+        this.__baseConstructorWasCalled = false;
     },
     handleMessage: function(msg){
         if (msg == getMethodSelf){
@@ -490,8 +523,16 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
             return this.__boundType;
         }
 
-        if (msg == getMethodSuper)
+        if (msg == getConstructorSuperMsg){
+            this.__baseConstructorWasCalled = true;
             return this.__handleSuperCall();
+        }
+
+        if (msg == getMethodSuper){
+            if (this.__isConstructor)
+                throw new Errors.Error("cannot call base constructor from procedure body (use '| SUPER' to pass parameters to base constructor)");
+            return this.__handleSuperCall();
+        }
 
         if (msg instanceof MethodOrProcMsg){
             var id = msg.id;
@@ -520,7 +561,9 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
     _beginBody: function(){
         Context.ProcDecl.prototype._beginBody.call(this);
         if (this.__isConstructor)
-            this.codeGenerator().write(this.__boundType.fieldsInitializationCode);
+            this.codeGenerator().write(
+                this.__boundType.baseConstructorCallCode
+              + this.__boundType.fieldsInitializationCode);
     },
     _makeArgumentVariable: function(arg){
         if (!arg.isVar)
@@ -565,9 +608,15 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
                         + this.__endingId + "'");
             }
 
-            if (this.__isConstructor)
+            if (this.__isConstructor){
+                var base = Type.recordBase(this.__boundType);
+                if (!this.__baseConstructorWasCalled && base && base.customConstructor && base.customConstructor.args().length)
+                    throw new Errors.Error("base record constructor has parameters but was not called (use '| SUPER' to pass parameters to base constructor)");
+                if (this.__baseConstructorWasCalled && (!base.customConstructor || !base.customConstructor.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)");
                 this.codeGenerator().write(
                     this.__boundType.defineConstructor(this.__methodType.procType()));
+            }
             else
                 this.__boundType.defineMethod(this.__methodId, this.__methodType);
         }
@@ -583,9 +632,13 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
                 + "' has no base type - SUPER cannot be used");
 
         var id = this.__methodId.id();
-        EberonRecord.requireMethodDefinition(baseType, id, "cannot use abstract method(s) in SUPER calls");
+        if (!this.__isConstructor)
+            EberonRecord.requireMethodDefinition(baseType, id, "cannot use abstract method(s) in SUPER calls");
+        
         return {
-            info: Type.makeProcedure(EberonTypes.makeMethodType(id, this.__methodType.procType(), superMethodCallGenerator)),
+            type: this.__boundType,
+            info: this.__isConstructor ? undefined
+                                       : Type.makeProcedure(EberonTypes.makeMethodType(id, this.__methodType.procType(), superMethodCallGenerator)),
             code: this.qualifyScope(Type.recordScope(baseType))
                 + Type.typeName(baseType) + ".prototype." + id + ".call"
         };
@@ -1088,6 +1141,7 @@ var ModuleDeclaration = Context.ModuleDeclaration.extend({
 exports.AddOperator = AddOperator;
 exports.ArrayDecl = ArrayDecl;
 exports.ArrayDimensions = ArrayDimensions;
+exports.BaseInit = BaseInit;
 exports.CaseLabel = CaseLabel;
 exports.ConstDecl = ConstDecl;
 exports.Designator = Designator;

+ 6 - 0
src/eberon/eberon_grammar.js

@@ -91,6 +91,11 @@ function makeFormalArray(){
     return and("ARRAY", optional("*"), "OF");
 }
 
+function makeFormalResult(base, actualParameters){
+    return or(base, 
+              context(and("|", "SUPER", actualParameters), EbContext.BaseInit));
+}
+
 function makeReturn(base){
     return and(base, optional(";"));
 }
@@ -106,6 +111,7 @@ exports.language = {
         makeForInit,
         makeArrayDimensions,
         makeFormalArray,
+        makeFormalResult,
         makeReturn,
         { 
             constDeclaration:   EbContext.ConstDecl, 

+ 2 - 1
src/grammar.js

@@ -31,6 +31,7 @@ function make(makeIdentdef,
               makeForInit,
               makeArrayDimensions,
               makeFormalArray,
+              makeFormalResult,
               makeReturn,
               contexts,
               reservedWords
@@ -177,7 +178,7 @@ var formalParameters = and(
           "("
         , optional(context(and(fpSection, repeat(and(";", fpSection))), Context.ProcParams))
         , required( ")" )
-        , optional(and(":", qualident)));
+        , optional(makeFormalResult(and(":", qualident), actualParameters)));
 
 var procedureType = and("PROCEDURE"
                       , context(optional(formalParameters), contexts.FormalParameters)

+ 3 - 3
src/ob/Types.ob

@@ -161,9 +161,9 @@ TYPE
         PROCEDURE finalize*();
 
         fields: JsMap.Type;
-        base-:   PRecord;
-        cons:   STRING;
-        scope:  ScopeBase.PType;
+        base-:  PRecord;
+        cons-:  STRING;
+        scope-: ScopeBase.PType;
         notExported: ARRAY * OF STRING
     END;
     

+ 5 - 0
src/oberon/oberon_grammar.js

@@ -71,6 +71,10 @@ function makeFormalArray(){
     return and("ARRAY", "OF");
 }
 
+function makeFormalResult(base){
+    return base;
+}
+
 function makeReturn(base){return base;}
 
 exports.language = {
@@ -84,6 +88,7 @@ exports.language = {
         makeForInit,
         makeArrayDimensions,
         makeFormalArray,
+        makeFormalResult,
         makeReturn,
         {
             constDeclaration:   Context.ConstDecl, 

+ 4 - 0
test/expected/eberon/constructor.js

@@ -25,6 +25,10 @@ function passAsArgument(o/*T*/){
 }
 function RecordWithParamConstructor(a/*INTEGER*/){
 }
+function DerivedRecordWithParamConstructor(){
+	RecordWithParamConstructor.call(this, 123);
+}
+RTL$.extend(DerivedRecordWithParamConstructor, RecordWithParamConstructor);
 passAsArgument(new T());
 var r = new T();
 var i = new RecordWithField().i;

+ 7 - 0
test/input/eberon/constructor.ob

@@ -16,6 +16,9 @@ TYPE
     RecordWithParamConstructor = RECORD
     END;
 
+    DerivedRecordWithParamConstructor = RECORD(RecordWithParamConstructor)
+    END;
+
 PROCEDURE T();
 END;
 
@@ -34,6 +37,10 @@ END;
 PROCEDURE RecordWithParamConstructor(a: INTEGER);
 END;
 
+PROCEDURE DerivedRecordWithParamConstructor()
+    | SUPER(123);
+END;
+
 BEGIN
     passAsArgument(T());
     r <- T();

+ 21 - 0
test/test_unit_eberon.js

@@ -1117,6 +1117,27 @@ exports.suite = {
                 "TYPE T = RECORD END;"),
         pass("r <- T()"),
         fail()
+        ),
+    "call base": testWithContext(
+        context(grammar.declarationSequence,
+                "TYPE T = RECORD END; RecordWthoutConstructor = RECORD END;"
+                + "Derived = RECORD(T) END;"
+                + "DerivedWthoutConstructor = RECORD(RecordWthoutConstructor) END;"
+                + "RecordWthConstructorNoParameters = RECORD END;"
+                + "DerivedWthConstructorNoParameters = RECORD(RecordWthConstructorNoParameters) END;"
+                + "PROCEDURE T(a: INTEGER); END;"
+                + "PROCEDURE RecordWthConstructorNoParameters(); END;"
+               ),
+        pass("PROCEDURE Derived() | SUPER(0); END;"),
+        fail(["PROCEDURE Derived(); END;", "base record constructor has parameters but was not called (use '| SUPER' to pass parameters to base constructor)"],
+             ["PROCEDURE Derived() | SUPER(1, 2); END;", "1 argument(s) expected, got 2"],
+             ["PROCEDURE Derived() | SUPER(FALSE); END;", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"],
+             ["PROCEDURE Derived() | SUPER(); END;", "1 argument(s) expected, got 0"],
+             ["PROCEDURE Derived(); BEGIN SUPER(0); END;", "cannot call base constructor from procedure body (use '| SUPER' to pass parameters to base constructor)"],
+             ["PROCEDURE RecordWthoutConstructor() | SUPER(0); END;", "'RecordWthoutConstructor' has no base type - SUPER cannot be used"],
+             ["PROCEDURE DerivedWthoutConstructor() | SUPER(); END;", "base record constructor has no parameters and will be called automatically (do not use '| SUPER' to call base constructor)"],
+             ["PROCEDURE DerivedWthConstructorNoParameters() | SUPER(); END;", "base record constructor has no parameters and will be called automatically (do not use '| SUPER' to call base constructor)"]
+            )
         )
     }
 };