Abfrage von TAA-Returncodes

Achtung, Baustelle!

In COBOL

In Cobol ist es nur über den Returncode auf einfache Art möglich, eine Fehlersituation im Programm abfragbar zu machen. Deshalb setzen alle EXEC TAA-Anweisungen, in denen eine Fehlersituation auftreten kann, den Returncode entweder als IM-RC-<Code> oder OM-RC-<Code>: OM-RC für Objektoperationen, IM-RC für andere. Alle IM-RC und OM-RC-Felder sind Bedingungsnamen (88er-Einträge) unter derselben Cobol-Variablen.

  • IM-RC-NOT-OK und OM-RC-NOT-OK sagen aus, dass die Anweisung entweder mit Fehler ausgeführt wurde, oder (im Fall von OM-RC-NOT-OK) das Ende der Liste erreicht wurde.
  • IM-RC-OK und OM-RC-OK sagen aus, dass die Anweisung erfolgreich ausgeführt wurde.
  • OM-RC-EOL sagt aus, dass die Anweisung zwar ausgeführt, aber das Ende der Liste erreicht wurde.

Eigentlich sollte für die Abfrage immer der Returncode abgefragt werden, der dem Anweisungstyp entspricht, also OM-RC für Objektoperationen, IM-RC für andere. Durch dass aber alle Returncodes unterhalb desselben Cobol-Felds definiert sind, blieb es in Cobol ohne Folge, wenn versehentlich der falsche Returncode abgefragt wurde, also z.B. OM-RC-ERROR statt IM-RC-ERROR.

In Cobol wird bei jeder EXEC-TAA Anweisung der Returncode zurückgesetzt. D.h. selbst wenn die Anweisung keinen Returncode setzt, kann es nicht vorkommen, dass nach einer EXEC TAA Anweisung ein Fehlercode einer vorhergehenden Anweisung noch vorliegt.

In Cobol ist es üblich, die weitere Verarbeitung vom Returncode einer Objektoperation abhängig zu machen, z.B.

   EXEC TAA GET FIRST MYOBJ END-EXEC
   IM OM-RC-EOL
   THEN
       SET TC-STATE-FEHLER OF ME TO TRUE
       EXEC TAA SET AND RAISE SEVERE GROUP MYGRP CODE 01
            ARGUMENTS(TC-LASTOM-OBJECT, TC-LASTOM-OPERATION)
       END-EXEC
    ELSE
    PERFORM SECTION-A
   END-IF

Außerdem gibt es in Cobol Variablen, die Namen und Art der zuletzt ausgeführten Objektoperation enthalten (s. obiges Beispiel, TC-LASTOM-OBJECT und -OPERATION).

In C#

In TAA-Modulen, die mit C# native implementiert sind, gibt es keine allgemeine Returncode-Variable. Normalerweise werden in Fehlersituationen Conditions aufgeworfen, die entweder im Programm behandelt werden können, oder zu einem Abbruch der Verarbeitung führen. Dieses Vorgehen macht das Bestücken von Returncode-Feldern sowie deren Abfrage überflüssig.

  • IM-RC-NOT-OK und OM-RC-NOT-OK: Wo in Cobol dieser RC gesetzt wurde, wird in C# eine Condition/Exception aufgeworfen.
  • IM-RC-OK und OM-RC-OK: Dies ist in C# der Normalfall, wenn eine Anweisung ohne Condition ausgeführt wurde.
  • OM-RC-EOL: Das Ergebnis einer Get-Operation ist null. Dies ist kein Fehler, sondern am Ende einer Liste zu erwarten.

Eine C#-Implementierung des o.g. Codes könnte damit z.B. so aussehen:

    var rec = MyObj.FirstOrDefault();
    if (rec == null) {
       this.State.Active = StateEnum.Fehler;
       this.Condition.New<Mldg_Aarch.MyGrp.Message1>(SeverityEnum.Severe, MyObj.Name, "Get").Raise();
    }
    SectionA();

Aus dem Cobol-Code im Rahmen der Migration solchen Code zu erzeugen, ist leider nicht möglich, da hierfür zu viele Annahmen über Inhalt und Auswirkung der mglw. kombinierten Bedingungen und verschachtelten Anweisungen getroffen werden müssten. Um trotzdem Code wie in obigem Cobol-Beispiel migrieren zu können, gibt es in migrierten Cobol-Modulen die Klasse LastOmOperation, die bei Objektoperationen bestückt wird und die Informationen über die zuletzt ausgeführte Objektoperation zur Verfügung stellt. Damit wird das Cobol-Beispiel in migriertem Code so aufgelöst:

    this.Data.MyObj.FirstOrDefault()?.CopyLocal(MyObj);
    if (this.LastOmOperation.IsEOL) {
       this.State.Active = StateEnum.Fehler;
       this.Condition.New<Mldg_Aarch.MyGrp.Message1>(SeverityEnum.Severe, LastOmOperation.ObjectName, LastOmOperation.Code).Raise();
    } else {
        SectionA();
    }

Die Klasse LastOmOperation wird jedoch nur für Objektoperationen bestückt, nicht für andere, z.B. Modulaufrufe, GevoProperty-Abfragen, usw. Das führte bereits zu der Situation, dass bei der Abfrage von OM-RC nach anderen als Objektoperationen fälschlicherweise der Returncode der letzen Objektoperation ausgewertet und die Verarbeitung fälschlicherweise abgebrochen wurde.

Bei der Abfrage von IM-RC stellt sich das Problem etwas anders dar, da es hierfür auch in migriertem C#-Code keinen Ersatz gibt. Da eine TAA-Anweisung, die keine Fehler-Condition erzeugt hat, erfolgreich ausgeführt wurde, hat die Abfrage von IM-RC-OK immer den Wert „true“, von IM-RC-ERROR immer den Wert „false“. In C#-Modulen führt dies bei jeder Abfrage von if (false) zu einer Warnung „warning CS0162: Unreachable code detected“.

Anpassungen bei Abfrage von nicht verfügbarem Returncode

Hiervon betroffene Abfragen sind solche, die den Returncode von nicht auf ein Taa-Objekt bezogenen Anweisungen auswerten (CALL, GET GEVO-PROPERTY, CONDITION, …), oder die nur Informationen über ein Objekt beschafft haben (GETINFO OBJECT).

Anhand der statischen Code-Analyse wird versucht, überflüssige Abfragen nicht zu generieren, und bei Abfragen, in denen der Erfolgsfall behandelt wird, darauf hinzuweisen, dass diese vereinfacht werden können. Falls die Returncode-Abfrage nicht entfernt werden kann, wird, um Fehler zu vermeiden, auf jeden Fall die Information in LastOmOperation zurückgesetzt, sodass die Abfrage des Returncodes immer OK liefern wird.

  • Wenn der Returncode in einer der direkt folgenden Anweisungen abgefragt wird, und die Abfrage nur den Fehlerfall behandelt, wird die Abfrage nicht generiert:

        EXEC TAA REFRESH TIMESTAMP END-EXEC
        IF NOT IM-RC-OK
            DISPLAY "notok"
        END-IF
     {
        TcTimestamp = this.Services.Timestamp.Now;
        // MIG_NOTE: dropped. The statement "IF NOT IM-RC-OK     ..." is dropped as the preceding TAA statement does not deliver a returncode.
    }

    Das gilt auch, wenn die Abfrage in einer eigenen Section steht und über Perform aufgerufen wird, sofern diese Section nichts anderes als die Abfrage auf RC-NOT-OK enthält:

        EXEC TAA REFRESH TIMESTAMP END-EXEC
        PERFORM OMRCCHECK
        .....
    OMRCCHECK SECTION.
        IF NOT OM-RC-OK
           EXEC TAA SET AND RAISE SEVERE GROUP MYGRP CODE 01
             ARGUMENTS(TC-LASTOM-OBJECT, TC-LASTOM-OPERATION)
           END-EXEC
        END-IF
        EXIT.
     {
        TcTimestamp = this.Services.Timestamp.Now;
        // MIG_NOTE: dropped. The statement "PERFORM OMRCCHECK" is dropped as the preceding TAA statement does not deliver a returncode.
    }
    private void Omrccheck()
    {
        if (!this.LastOmOperation.IsOK) {
            this.State.Active = StateEnum.Fehler;
            this.Condition.New<Mldg_Aarch.MyGrp.Message1>(SeverityEnum.Severe, LastOmOperation.ObjectName, LastOmOperation.Code).Raise();
        }
    }
  • Wenn der Returncode an einer oder mehreren Stellen in den möglichen Folge-Pfaden abgefragt wird: Um zu verhindern, dass ein altes Ergebnis abgefragt wird, wird LastOmOperation zurückgesetzt.

    	EXEC TAA GETINFO CONDITION(WS-NUM) END-EXEC
    	this.CurrentCondition = Condition?.AllConditions?.ElementAtOrDefault((int) (WsNum - 1));
    	this.LastOmOperation.Reset();
  • Wenn der Returncode in einer der direkt folgenden Anweisungen abgefragt wird, und erkennbar ist, dass die Abfrage nur die beiden Fälle OK und Nicht-OK behandelt, schlägt die Migration vor, die Abfrage auf den True-Zweig zu reduzieren:

          EXEC TAA GETINFO PGFORML
               ITEMS INTO PGFORMT-000-PG-FRACT
         END-EXEC
         IF (NOT OM-RC-OK)
             DISPLAY "ERROR"
         ELSE
             DISPLAY "SUCCESS"
         END-IF

    Der Zweig zu NOT OM-RC-OK wird in C# nie durchlaufen werden und kann deshalb entfallen. Damit die noch vorhandene IF-Anweisung kein falsches Ergebnis liefern kann, wird im geneierten C# der Returncode zurückgesetzt. Wenn die IF-Anweisung bis auf den Inhalt des ELSE-Zweigs entfernt wird, kann auch LastOmOperation.Reset() entfernt werden:

        Data.Pgformt._000PgFract = this.Data.Pgforml.Count;
        // MIG_NOTE: The previous TAA statement does not deliver a returncode, but is directly followed by an IM-RC, OM-RC or TX-RC check.
        // MIG_NOTE: To avoid false results by that check, LastOmOperation is reset.
        // MIG_NOTE: LastOmOperation.Reset can be removed if the check is adapted or removed .
        this.LastOmOperation.Reset();
        // MIG_NOTE: The following IF can be replaced by its Else branch, and the prepended LastOmOperation.Reset() can be removed.
        if (!this.LastOmOperation.IsOK) {
            Console.WriteLine(@"error");
        }
        else {
            Console.WriteLine(@"success");
        }

Die Hinweise werden mit MIG_NOTE versehen, und können bei entsprechenden Einstellungen in VisualStudio/Resharper über die Aufgabenliste abgearbeitet werden: mig_check_todo.jpg

cobmig:cs:procdiv:rccheck · Zuletzt geändert: 09.08.2024 13:24

Copyright © 1992-2024 TeamWiSE Gesellschaft für Softwaretechnik mbH         Adressen |  Kontakt |  AGB |  Datenschutzerklärung |  Impressum