RPGLE %PROC のアップデート

**IBM i 7.5 - Enhancements timed with Technology Refresh 5 (TR5)** で
前に書いた記事 [新命令ON-EXIT](https://qiita.com/ushiday/items/7c4c63625283c77e828c) の中で `%PROC関数（プロシージャの取得）` が使えるようになったとのことなので試してみました。

## 関連リンク
- [IBM i 7.5 - Enhancements timed with Technology Refresh 5 (TR5)](https://www.ibm.com/support/pages/ibm-i-75-tr5-enhancements)
- [RPG Cafe: Fall 2024: Use %PROC() in ON-EXIT](https://www.ibm.com/support/pages/node/7172104)
- [%PROC (現行プロシージャーの戻り値の名前)](https://www.ibm.com/docs/ja/i/7.5?topic=functions-proc-return-name-current-procedure)

## TR導入
通常の手順で V7R5-TR5 を導入したのですが、今回のサンプルコードはコンパイルエラーとなってしまいました。該当のPTF番号を `DSPPTF` で確認してもやはり導入されておりませんでした。
今回TRの対象個別PTF `SJ02921 ～ SJ02923` を個別オーダーし導入したら、動作するようになりましたので、TR5の導入でうまく行かない場合は個別を試してみて下さい。

![スクリーンショット 2024-12-05 184927.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100029/8541edee-8030-b0ed-103c-228373c8978e.png)

## 【注意事項】SJ02921 を入れた際の
今回の `%PROC` とは直接関係ありませんが、TR5で案内されている **SJ02921 - RPGLE-CMPL NEW DATEYY PARAMETER FOR CRTRPGMOD AND CRTBNDRPG** の個別PTFを導入した場合は、コマンド `CRTBNDRPG` と `CRTRPGMOD` の省略値を `CHGCMDDFT` で独自変更している場合はリセットされますので注意して下さい。私の環境も `DBGVIEW(*ALL)` としていたのですが、リセットされておりました。

## %PROCのサンプルコード

- [ushiday - qiita-rpgle %PROC ON-EXIT ](https://github.com/ushiday/qiita-rpgle/blob/main/QRPGSRC/Q242101R.RPGLE)

以下が実際に　**ON-EXIT** 内で `%PROC` で使用したサンプルコード例です。

```RPGLE.Q242101R.RPGLE
**FREE
//   H**********************************************************************
//   H*                                                                    *
//   H*   システム名      :ＲＰＧ教育                                *
//   H*   サブシステム名  :教育プログラム　　　　　　　　　　　      *
//   H*   プログラム名    :％ＰＲＯＣのＯＮ−ＥＸＩＴ内での使用      *
//   H*   プログラムＩＤ  : Q242101R                                   *
//   H*   会　社　名　　  :株式会社中部システム                      *
//   H*                                                                    *
//   H*     作　成　者    :㈱中部システム Y.USHIDA                   *
//   H*     作　成　日    : 2024/12/02                                 *
//   H*     管　理　番　号: ____________                               *
//   H*                                                                    *
//   H*     変　更　者    :　　　　　　　　　　　　　　　　　　      *
//   H*     変　更　日    : ____/__/__                                 *
//   H*     管　理　番　号:                                            *
//   H*                                                                    *
//   H*  プログラム特記事項                                              *
//   H* 　　　　　　　　　　　＿　　　　　　　　　　　　　　　　　　   *
//   H* 　　　　　　　　　　　＿　　　　　　　　　　　　　　　　　　   *
//   H*                                                                    *
//   H*-*******************************************************************
//   H*-*Ｈ仕様書                                                      **
//   H*-*******************************************************************
CTL-OPT
       DFTACTGRP(*NO) ACTGRP(*NEW)
       DATEDIT(*YMD)
       DECEDIT('0.')
       EXPROPTS(*RESDECPOS)
       CURSYM('¥')
       COPYRIGHT('...')
;
//   D*-********************************************************************
//   D*-* ＳＤＳ宣言                                                    **
//   D*-********************************************************************
//   https://www.ibm.com/docs/ja/i/7.5?topic=exceptionerrors-program-status-data-structure
DCL-DS *N PSDS;
       S#PGM               *PROC                         ;                    //プログラム名
       S#STS               *STATUS                       ;                    //状況コード
       S#ROUTINE           *ROUTINE                      ;                    //ルーチン名
       S#JOB               CHAR(10)     POS(244)         ;                    //ジョブ名
       S#USER              CHAR(10)     POS(254)         ;                    //ユーザー名
       S#JNBR              ZONED(6:0)   POS(264)         ;                    //ジョブ№
END-DS;
//   D*-********************************************************************
//   D*-* 変数等宣言                                                    **
//   D*-********************************************************************
DCL-S  W#RTN         CHAR(040      )          ;  //結果
//   C*-********************************************************************
//   C*-* メインルーチン                                                **
//   C*-********************************************************************

W#RTN       = SUBPROC1(99999          )       ;
DSPLY         W#RTN                           ;


W#RTN       = SUBPROC1(90000          )       ;
DSPLY         W#RTN                           ;


*INLR       = *ON                             ;
RETURN                                        ;

//   P*-********************************************************************
//   P*-* サブプロシージャ（１）                                        **
//   P*-********************************************************************
DCL-PROC SUBPROC1                               ;
//インターフェース
  DCL-PI *N   CHAR(001      )                   ;  //Ｓ＝成功、Ｅ＝失敗
     IN_CODE          PACKED(005 :000) VALUE    ;  //コード
  END-PI;
  //   F*-*******************************************************************
  //   F*-*ファイル宣言                                                  **
  //   F*-*******************************************************************
  DCL-F    ITEM          KEYED    USAGE(*UPDATE : *OUTPUT     )                ;
    DCL-DS   R#ITEMR                LIKEREC(ITEMR       :*ALL         )          ;
    DCL-DS   K#ITEM                 LIKEREC(ITEMR       :*KEY         )          ;
  //   D*-********************************************************************
  //   D*-* 変数等宣言                                                    **
  //   D*-********************************************************************
  DCL-S  W#MSG         CHAR(040      )          ;  //メッセージ
  DCL-S  W#ZERO        PACKED(003 :000   )      ;
  DCL-S  W#NOMON       IND                      ;  //＊ＯＮ＝監視なしの終了
  DCL-S  W#PGM1        CHAR(010      )          ;  //プログラム名
  DCL-S  W#PROC1       CHAR(040      )          ;  //プロシージャ名
  DCL-S  W#PROC2       CHAR(040      )          ;  //プロシージャ名
  DCL-S  W#PROC3       CHAR(040      )          ;  //プロシージャ名

//プログラム名取得
  W#PGM1      = S#PGM                           ;

//サブプロシージャ名取得
  W#PROC1     = %PROC()                         ;

//サブロジック
  K#ITEM.CODE = IN_CODE                         ;

  MONITOR                                       ;
    CHAIN %KDS(K#ITEM    :01 ) ITEMR     R#ITEMR  ;
    IF %FOUND                                     ;
      WRITE   ITEMR     R#ITEMR                     ;
    ENDIF                                         ;
  ON-ERROR  *ALL                                ;
    W#MSG   = '[ERROR:*ALL]'
            + %CHAR(%STATUS     )
            + '*ALL監視エラー'                  ;
    DSPLY     W#MSG                               ;
    RETURN    'E'                                 ;
  ENDMON                                        ;


  W#ZERO      = *ZERO                           ;
  W#ZERO      = 100     / W#ZERO                ;

  RETURN    'S'                                 ;

//必ずサブプロシージャで通過する終了処理
  ON-EXIT   W#NOMON                             ;

  //ＯＮ−ＥＸＩＴを実装実行しているサブプロシージャ名
  W#PROC2     = %PROC(*ONEXIT        )          ;

  //ＯＮ−ＥＸＩＴの実装呼び出し元サブプロシージャ名（※このソース）
  W#PROC3     = %PROC(*OWNER         )          ;

    W#MSG   = '[S#PGM][S#ROUTINE]'
            + %TRIM(S#PGM  )
            + ' ' + S#ROUTINE                     ;
    DSPLY     W#MSG                               ;

    W#MSG   = '[W#PROC1]'
            + %TRIM(W#PGM1  )
            + ' ' + W#PROC1                       ;
    DSPLY     W#MSG                               ;

    W#MSG   = '[W#PROC2]'
            + %TRIM(W#PGM1  )
            + ' ' + W#PROC2                       ;
    DSPLY     W#MSG                               ;

    W#MSG   = '[W#PROC3]'
            + %TRIM(W#PGM1  )
            + ' ' + W#PROC3                       ;
    DSPLY     W#MSG                               ;

    IF %FOUND(ITEM        )                       ;
      UNLOCK ITEM                                   ;
      W#MSG   = '[UNLOCK]'
              + 'レコードロック解放'              ;
      DSPLY     W#MSG                               ;
    ENDIF                                         ;
    W#MSG   = '[EXIT]'
            + '全ての処理が通過する'
            + %CHAR(W#NOMON    )                  ;
    DSPLY     W#MSG                               ;

//監視外の例外が発生している場合
    IF W#NOMON                                    ;
      //ログなど...
      RETURN    'E'                                 ;
    ENDIF                                         ;

END-PROC;


```

以下変数は、それぞれサンプルプログラムにおいて別のタイミングやパラメータで `%PROC関数` を用いてプロシージャ名を取得しています。

|変数名|取得タイミングやパラメータ|
|---|---|
|W#PROC1|サブプロシージャの本体ルーチンで `%PROC()`|
|W#PROC2|サブプロシージャのON-EXITルーチンで `%PROC(*ONEXIT)`|
|W#PROC3|サブプロシージャのON-EXITルーチンで `%PROC(*OWNER)`|

プログラムを実行すると次の結果が得られます。

![2024-12-17-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100029/2ecb428f-0f2c-cfca-fa8a-22b4c10d3a5d.png)

## 実行結果を考察

**W#PROC1** は、サブプロシージャの本体ルーチンで `%PROC()` を実行しているのでプロシージャ名である **SUBPROC1** が返されます。これは従来より可能でした、次からは新しく実装された機能になります。

**W#PROC2** は、サブプロシージャの `ON-EXIT` で `%PROC(*ONEXIT)` を実行しているので、実際に `ON-EXIT` を処理しているプロシージャ名である **_QRNI_ON_EXIT_SUBPROC1** が返されます。通常このプロシージャ名を我々開発者は特に意識していません。

**W#PROC3** は、サブプロシージャの `ON-EXIT` で `%PROC(*OWNER)` を実行しているので、`ON-EXIT` の呼び出し元プロシージャ名である **SUBPROC1** が返されます。これは我々開発者が意識しているプロシージャ名です。
`ON-EXIT` 内の一般的なログの収集等に使われるプロシージャ名は `%PROC(*OWNER)` になるでしょう。

ここからは想像ですが、ソースコード内に `ON-EXIT` を実装したプロシージャが存在すると、ILE コンパイラーは `ON-EXIT` 以下を部分的に抜き出し、つづいてその内容を必須処理ルーチンとした別プロシージャ **_QRNI_ON_EXIT_プロシージャ名** を自動生成しているのでは？と考えてます。

実際に `DSPPGM PGM(Q242101R)` コマンドで確認すると、`ON-EXIT` **なし**と**あり**では**プロシージャ数に1つ分の違い**があらわれます。

![2024-12-17-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100029/30775631-b2a3-830c-968a-29d971845523.png)

![2024-12-17-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100029/7fdf24b6-2796-9bd2-fd39-48efc0882183.png)


モジュールのプロシージャの建付けは以下の様な感じでしょうか？本来フラットな筈のサブプロシージャに `ON-EXIT` だけは階層があるように見えます。
```.自動生成されたON-EXITルーチンの階層イメージ
　メインプロシージャ（Q242101R）
    └ サブプロシージャ（SUBPROC1）*OWNER
        └ ON-EXIT プロシージャ（_QRNI_ON_EXIT_SUBPROC1）*ONEXIT
```

複数のプロシージャで `ON-EXIT` を実装したり何パターンか試してみましたが、ちゃんと **ON-EXIT プロシージャ** が孫階層的になっており、変数スコープも維持されておりました。面白いですね (^^)
ILE 内部では変数スコープ維持を頑張っているのか？それとも3段目の特別なプロシージャとなっているのか？新しい機能のそれ自体にも興味がありますが、それがどう実装されているのか？想像するのもまた技術者としては楽しいです。**RPG** は日々進化しています！