m3front/src/stmts/TryFinStmt.m3


 Copyright (C) 1992, Digital Equipment Corporation           
 All rights reserved.                                        
 See the file COPYRIGHT for a full description.              
                                                             
 File: TryFinStmt.m3                                         
 Last modified on Fri May 19 07:50:09 PDT 1995 by kalsow     
      modified on Thu Dec  5 17:19:13 PST 1991 by muller     

MODULE TryFinStmt;

IMPORT M3ID, CG, Token, Scanner, Stmt, StmtRep, Marker, Target, Type, Addr;
IMPORT RunTyme, Procedure, ProcBody, M3RT, Scope, Fmt, Host, TryStmt, Module;
FROM Stmt IMPORT Outcome;

TYPE
  P = Stmt.T OBJECT
        body     : Stmt.T;
        finally  : Stmt.T;
        forigin  : INTEGER;
        viaProc  : BOOLEAN;
        scope    : Scope.T;
        handler  : HandlerProc;
      OVERRIDES
        check       := Check;
        compile     := Compile;
        outcomes    := GetOutcome;
      END;

TYPE
  HandlerProc = ProcBody.T OBJECT
    self: P;
    activation: CG.Var;
  OVERRIDES
    gen_decl := EmitDecl;
    gen_body := EmitBody;
  END;

VAR
  last_name : INTEGER := 0;
  next_uid  : INTEGER := 0;

PROCEDURE Parse (body: Stmt.T;  ): Stmt.T =
  TYPE TK = Token.T;
  VAR p := NEW (P);
  BEGIN
    StmtRep.Init (p);
    p.body := body;
    Scanner.Match (TK.tFINALLY);
    p.forigin := Scanner.offset;
    IF Target.Has_stack_walker THEN
      p.viaProc := FALSE;
      p.scope   := NIL;
      p.finally := Stmt.Parse ();
    ELSE
      p.handler := NEW (HandlerProc, self := p);
      ProcBody.Push (p.handler);
      p.scope := Scope.PushNew (TRUE, M3ID.NoID);
      p.finally := Stmt.Parse ();
      Scope.PopNew ();
      ProcBody.Pop ();
    END;
    Scanner.Match (TK.tEND);
    RETURN p;
  END Parse;

PROCEDURE Check (p: P;  VAR cs: Stmt.CheckState) =
  VAR zz: Scope.T;  oc: Stmt.Outcomes;  name: INTEGER;
  BEGIN
    Marker.PushFinally (CG.No_label, CG.No_label, NIL);
    Stmt.TypeCheck (p.body, cs);
    Marker.Pop ();
    TryStmt.PushHandler (NIL, 0, FALSE);
    IF Target.Has_stack_walker THEN
      Stmt.TypeCheck (p.finally, cs);
    ELSE
      oc := Stmt.GetOutcome (p.finally);
      IF (Stmt.Outcome.Exits IN oc) OR (Stmt.Outcome.Returns IN oc) THEN
        p.viaProc := FALSE;
        Stmt.TypeCheck (p.finally, cs);
      ELSE
        p.viaProc := TRUE;
        name := p.forigin MOD 10000;
        p.handler.name := HandlerName (name);
        IF (name = last_name) THEN
          INC (next_uid);
          p.handler.name := p.handler.name & "_" & Fmt.Int (next_uid);
        ELSE
          last_name := name;
          next_uid := 0;
        END;
        zz := Scope.Push (p.scope);
          Scope.TypeCheck (p.scope, cs);
          Stmt.TypeCheck (p.finally, cs);
        Scope.Pop (zz);
      END;
    END;
    TryStmt.PopHandler ();
  END Check;

PROCEDURE HandlerName (uid: INTEGER): TEXT =
  CONST Insert = ARRAY BOOLEAN OF TEXT { "_M3_LINE_", "_I3_LINE_" };
  BEGIN
    RETURN M3ID.ToText (Module.Name (NIL))
           & Insert [Module.IsInterface ()]
           & Fmt.Int (uid);
  END HandlerName;

PROCEDURE Compile (p: P): Stmt.Outcomes =
  BEGIN
    IF Target.Has_stack_walker THEN RETURN Compile1 (p);
    ELSIF p.viaProc            THEN RETURN Compile2 (p);
    ELSE                            RETURN Compile3 (p);
    END;
  END Compile;

PROCEDURE Compile1 (p: P): Stmt.Outcomes =
  VAR
    oc, xc, o: Stmt.Outcomes;
    l: CG.Label;
    info: CG.Var;
    proc: Procedure.T;
  BEGIN
    (* declare and initialize the info record *)
    info := CG.Declare_local (M3ID.NoID, M3RT.EA_SIZE, Target.Address.align,
                              CG.Type.Struct, 0, in_memory := TRUE,
                              up_level := FALSE, f := CG.Never);
    CG.Load_nil ();
    CG.Store_addr (info, M3RT.EA_exception);

    (* compile the body *)
    l := CG.Next_label (2);
    CG.Set_label (l, barrier := TRUE);
    Marker.PushFinally (l, l+1, info);
    Marker.SaveFrame ();
      oc := Stmt.Compile (p.body);
    Marker.Pop ();
    CG.Set_label (l+1, barrier := TRUE);

    (* set the "Compiler.ThisException()" globals *)
    TryStmt.PushHandler (info, 0, direct := TRUE);

    (* compile the handler *)
    Scanner.offset := p.forigin;
    CG.Gen_location (p.forigin);
      xc := Stmt.Compile (p.finally);

    (* generate the bizzare end-tests *)

    IF (Outcome.Returns IN oc) THEN
      l := CG.Next_label ();
      CG.Load_int (Target.Integer.cg_type, info, M3RT.EA_exception);
      CG.Load_intt (Marker.Return_exception);
      CG.If_compare (Target.Integer.cg_type, CG.Cmp.NE, l, CG.Always);
      Marker.EmitReturn (NIL, fromFinally := TRUE);
      CG.Set_label (l);
    END;

    IF (Outcome.Exits IN oc) THEN
      l := CG.Next_label ();
      CG.Load_int (Target.Integer.cg_type, info, M3RT.EA_exception);
      CG.Load_intt (Marker.Exit_exception);
      CG.If_compare (Target.Integer.cg_type, CG.Cmp.NE, l, CG.Always);
      Marker.EmitExit ();
      CG.Set_label (l);
    END;

    (* resume the exception *)
    proc := RunTyme.LookUpProc (RunTyme.Hook.ResumeRaiseEx);
    l := CG.Next_label ();
    CG.Load_addr (info, M3RT.EA_exception);
    CG.Load_nil ();
    CG.If_compare (CG.Type.Addr, CG.Cmp.EQ, l, CG.Always);
    Procedure.StartCall (proc);
    CG.Load_addr_of (info, 0, Target.Address.align);
    CG.Pop_param (CG.Type.Addr);
    Procedure.EmitCall (proc);
    CG.Set_label (l);

    (* restore the "Compiler.ThisException()" globals *)
    TryStmt.PopHandler ();

    o := Stmt.Outcomes {};
    IF Outcome.FallThrough IN xc THEN o := oc END;
    IF Outcome.Exits IN xc   THEN o := o + Stmt.Outcomes {Outcome.Exits} END;
    IF Outcome.Returns IN xc THEN o := o + Stmt.Outcomes {Outcome.Returns} END;
    RETURN o;
  END Compile1;

PROCEDURE Compile2 (p: P): Stmt.Outcomes =
  VAR
    oc, xc, o: Stmt.Outcomes;
    l: CG.Label;
    frame: CG.Var;
  BEGIN
    <*ASSERT p.viaProc*>

    (* declare and initialize the info record *)
    frame := CG.Declare_local (M3ID.NoID, M3RT.EF2_SIZE, Target.Address.align,
                               CG.Type.Struct, 0, in_memory := TRUE,
                               up_level := FALSE, f := CG.Never);
    CG.Load_procedure (p.handler.cg_proc);
    CG.Store_addr (frame, M3RT.EF2_handler);
    CG.Load_static_link (p.handler.cg_proc);
    CG.Store_addr (frame, M3RT.EF2_frame);

    (* compile the body *)
    l := CG.Next_label (2);
    CG.Set_label (l, barrier := TRUE);
    Marker.PushFrame (frame, M3RT.HandlerClass.FinallyProc);
    Marker.PushFinallyProc (l, l+1, frame, p.handler.cg_proc, p.handler.level);
      oc := Stmt.Compile (p.body);
    Marker.Pop ();
    IF (Outcome.FallThrough IN oc) THEN
      Marker.PopFrame (frame);
      CG.Start_call_direct (p.handler.cg_proc, p.handler.level, CG.Type.Void);
      (* Shouldn't we pass the activation parameter here?
         What value do we pass? *)
      CG.Call_direct (p.handler.cg_proc, CG.Type.Void);
    END;
    CG.Set_label (l+1, barrier := TRUE);

    (* set the "Compiler.ThisException()" globals *)
    TryStmt.PushHandler (p.handler.activation, 0, direct := FALSE);

    Scanner.offset := p.forigin;
    CG.Gen_location (p.forigin);
    IF (Host.inline_nested_procs) THEN
      CG.Begin_procedure (p.handler.cg_proc);
      xc := Stmt.Compile (p.finally);
      CG.Exit_proc (CG.Type.Void);
      CG.End_procedure (p.handler.cg_proc);
    ELSE
      CG.Note_procedure_origin (p.handler.cg_proc);
      xc := Stmt.GetOutcome (p.finally);
    END;

    (* restore the "Compiler.ThisException()" globals *)
    TryStmt.PopHandler ();

    o := Stmt.Outcomes {};
    IF Outcome.FallThrough IN xc THEN o := oc END;
    IF Outcome.Exits IN xc   THEN o := o + Stmt.Outcomes {Outcome.Exits} END;
    IF Outcome.Returns IN xc THEN o := o + Stmt.Outcomes {Outcome.Returns} END;
    RETURN o;
  END Compile2;

PROCEDURE EmitDecl (x: HandlerProc) =
  VAR p := x.self;  par: CG.Proc := NIL;
  BEGIN
    IF (p.viaProc) THEN
      IF (x.parent # NIL) THEN par := x.parent.cg_proc; END;
      x.cg_proc := CG.Declare_procedure (M3ID.Add (x.name), 1, CG.Type.Void,
                                         x.level, Target.DefaultCall,
                                         exported := FALSE, parent := par);
      x.activation := CG.Declare_param (M3ID.NoID, Target.Address.size,
                                        Target.Address.align, CG.Type.Addr,
                                        Type.GlobalUID (Addr.T),
                                        in_memory := FALSE, up_level := FALSE,
                                        f := CG.Always);
    END;
  END EmitDecl;

PROCEDURE EmitBody (x: HandlerProc) =
  VAR p := x.self;
  BEGIN
    IF (p.viaProc) AND (NOT Host.inline_nested_procs) THEN

      (* set the "Compiler.ThisException()" globals *)
      TryStmt.PushHandler (x.activation, 0, direct := FALSE);

      (* generate the actual procedure *)
      Scanner.offset := p.forigin;
      CG.Gen_location (p.forigin);
      CG.Begin_procedure (x.cg_proc);
      EVAL Stmt.Compile (p.finally);
      CG.Exit_proc (CG.Type.Void);
      CG.End_procedure (x.cg_proc);

      (* restore the "Compiler.ThisException()" globals *)
      TryStmt.PopHandler ();

    END;
  END EmitBody;

PROCEDURE Compile3 (p: P): Stmt.Outcomes =
  VAR
    oc, xc, o: Stmt.Outcomes;
    l, xx: CG.Label;
    frame: CG.Var;
    returnSeen, exitSeen: BOOLEAN;
    proc: Procedure.T;
  BEGIN
    <* ASSERT NOT p.viaProc *>

    (* declare and initialize the info record *)
    frame := CG.Declare_local (M3ID.NoID, M3RT.EF1_SIZE, M3RT.EF1_ALIGN,
                               CG.Type.Struct, 0, in_memory := TRUE,
                               up_level := FALSE, f := CG.Never);
    CG.Load_nil ();
    CG.Store_addr (frame, M3RT.EF1_info + M3RT.EA_exception);

    l := CG.Next_label (3);
    CG.Set_label (l, barrier := TRUE);
    Marker.PushFrame (frame, M3RT.HandlerClass.Finally);
    Marker.CaptureState (frame, l+1);

    (* compile the body *)
    Marker.PushFinally (l, l+1, frame);
      oc := Stmt.Compile (p.body);
    Marker.PopFinally (returnSeen, exitSeen);
    IF (Outcome.FallThrough IN oc) THEN
      Marker.PopFrame (frame);
    END;
    CG.Set_label (l+1, barrier := TRUE);

    (* set the "Compiler.ThisException()" globals *)
    TryStmt.PushHandler (frame, M3RT.EF1_info, direct := TRUE);

    (* compile the handler *)
    Scanner.offset := p.forigin;
    CG.Gen_location (p.forigin);
    xc := Stmt.Compile (p.finally);

    IF (Outcome.FallThrough IN xc) THEN
      (* generate the bizzare end-tests *)

      (* exceptional outcome? *)
      CG.Load_addr (frame, M3RT.EF1_info + M3RT.EA_exception);
      CG.Load_nil ();
      CG.If_compare (CG.Type.Addr, CG.Cmp.EQ, l+2, CG.Always);

      IF (exitSeen) THEN
        xx := CG.Next_label ();
        CG.Load_int (Target.Integer.cg_type,
                     frame, M3RT.EF1_info + M3RT.EA_exception);
        CG.Load_intt (Marker.Exit_exception);
        CG.If_compare (Target.Integer.cg_type, CG.Cmp.NE, xx, CG.Always);
        Marker.EmitExit ();
        CG.Set_label (xx);
      END;

      IF (returnSeen) THEN
        xx := CG.Next_label ();
        CG.Load_int (Target.Integer.cg_type,
                     frame, M3RT.EF1_info + M3RT.EA_exception);
        CG.Load_intt (Marker.Return_exception);
        CG.If_compare (Target.Integer.cg_type, CG.Cmp.NE, xx, CG.Always);
        Marker.EmitReturn (NIL, fromFinally := TRUE);
        CG.Set_label (xx);
      END;

      (* ELSE, a real exception is being raised => resume it *)
      proc := RunTyme.LookUpProc (RunTyme.Hook.ResumeRaiseEx);
      Procedure.StartCall (proc);
      CG.Load_addr_of (frame, M3RT.EF1_info, Target.Address.align);
      CG.Pop_param (CG.Type.Addr);
      Procedure.EmitCall (proc);

      CG.Set_label (l+2, barrier := TRUE);
    END;

    (* restore the "Compiler.ThisException()" globals *)
    TryStmt.PopHandler ();

    o := Stmt.Outcomes {};
    IF Outcome.FallThrough IN xc THEN o := oc END;
    IF Outcome.Exits IN xc   THEN o := o + Stmt.Outcomes {Outcome.Exits} END;
    IF Outcome.Returns IN xc THEN o := o + Stmt.Outcomes {Outcome.Returns} END;
    RETURN o;
  END Compile3;

PROCEDURE GetOutcome (p: P): Stmt.Outcomes =
  VAR oc, xc, o: Stmt.Outcomes;
  BEGIN
    oc := Stmt.GetOutcome (p.body);
    xc := Stmt.GetOutcome (p.finally);
    o := Stmt.Outcomes {};
    IF Outcome.FallThrough IN xc THEN o := oc END;
    IF Outcome.Exits IN xc THEN o := o + Stmt.Outcomes {Outcome.Exits} END;
    IF Outcome.Returns IN xc THEN o := o + Stmt.Outcomes {Outcome.Returns} END;
    RETURN o;
  END GetOutcome;

BEGIN
END TryFinStmt.

interface M3ID is in:


interface Token is in:


interface Marker is in:


interface Type is in: