メインフレームの遺産と対峙する:PL/IにおけるGOTOの呪縛と「構造化」の真意
基幹システムの保守・更改現場に身を置いていると、必ずと言っていいほど「悪魔のコード」に出くわす。`GOTO`文が縦横無尽に飛び交い、変数のスコープが迷宮入りしたPL/Iプログラムだ。現代のJavaやC#の感覚でこれを読み解こうとすると、十中八九、迷宮の奥底で道を見失うことになる。
PL/Iの歴史は長い。初期のコンパイラから最新のEnterprise PL/Iに至るまで、この言語は「何でもできる」という強力な武器を我々に与えてくれたが、同時に「何でもできてしまう」というリスクを負わせた。特に`GOTO`文のスコープ制限と、それを無視して設計されたスパゲッティコードが、現在のマイグレーションプロジェクトにおいてどれほどの技術的負債となっているか、今一度深掘りしてみよう。
—
1. GOTOのスコープ制限と「戻れない道」
PL/Iにおいて、`GOTO`文は同じブロック内、あるいは親ブロックへのジャンプを許容する。しかし、この柔軟性が災いし、特に複雑な`PROCEDURE`や`BEGIN`ブロックの入れ子構造の中で、動的なラベルジャンプが行われると、制御フローの解析は絶望的になる。
なぜ保守性が崩壊するのか
構造化プログラミングの鉄則を無視したコードでは、アベンド(ABEND)発生時にダンプを追うのが困難だ。`GOTO`で制御が移った先で、本来解放されるべき動的メモリ(`BASED`変数)が解放されず、そのまま処理が終了すればメモリリークが発生する。CICS環境であれば、これが数万件のトランザクションを重ねた末のS0C4アベンドという「時限爆弾」へと化ける。
/i
/ 不適切なGOTOによる制御フローの例 /
PROC_MAIN: PROC OPTIONS(MAIN);
DCL PTR_DATA POINTER;
DCL 1 MY_REC BASED(PTR_DATA),
2 FIELD_A CHAR(10),
2 FIELD_B FIXED DEC(9,0);
ALLOCATE MY_REC SET(PTR_DATA);
/ 本来ならここでFREEすべきメモリを無視してジャンプ /
GOTO ERROR_LABEL;
/ 正常処理 /
FREE MY_REC;
RETURN;
ERROR_LABEL:
/ ここでDB2のSQLCODEをチェックしようとすると、
すでに変数のスコープが終了しており不正アクセスになる可能性がある /
PUT SKIP LIST(‘ABEND発生時のダンプ解析では、このラベルへのジャンプ元が不明瞭になる’);
END PROC_MAIN;
—
2. マイグレーションを阻む「内部構造」の闇
JavaやC#へ移行する際、最も頭を抱えるのが「パックデシマル(`FIXED DEC`)」の扱いと、ポインタによるメモリ直接操作だ。
パックデシマルの符号反転バグ
PL/Iの`FIXED DEC`は、内部的にニブル(4ビット)単位で数値を保持する。稀に、古いCOBOLから受け継いだデータや、バイナリ転送時のエンディアン誤解により、最後の桁の符号ビットが反転し、数値が計算結果としてあり得ない値(あるいは符号が逆転)になることがある。Javaへの移行時に`BigDecimal`でラップするだけでは不十分だ。必ず「データ変換層」にて、メインフレーム側の表現形式を厳密にエミュレートする変換ロジックを噛ませる必要がある。
ポインタとBASED変数の罠
C系言語への移行時に最も苦労するのが、PL/Iの`BASED`変数によるメモリの重ね書き(REDEFINES的なアプローチ)だ。
「このポインタが指す先は、実は別の構造体としても参照されている」というコードは、現代的なオブジェクト指向言語ではカプセル化の概念と真っ向から衝突する。
—
3. アベンド解析の現場:アーキテクトの視点
基幹システムでS0C7(データ例外)やS0C4(保護例外)が発生した際、我々が見るのはソースコードだけではない。コンパイラ最適化オプション(`OPTIMIZE(3)`など)によって、機械語レベルで命令の順序が入れ替わっている可能性を考慮しなければならない。
- コンパイラ最適化の副作用:
最適化をかけると、デバッガ上でブレークポイントが期待した行で止まらないことがある。これが「デバッグを困難にする要因」の一つだ。
- ダンプ解析の定石:
まずは`CEE3DMP`(Language Environmentのダンプ出力)を確認すること。特に`GOTO`で飛ばされた先の「無効なスタックフレーム」を特定し、その直前のプロシージャ呼び出し履歴から、どの変数が破壊されたのかを逆算する。
—
結論:コードを「再構築」する勇気
マイグレーションは単なる言語の置き換えではない。レガシーシステムの「論理的な構造」を抽出し、それを現代的なアーキテクチャに再配置する作業だ。
`GOTO`文の除去は、単にコードを綺麗にするためではない。「予測可能な実行フロー」を取り戻し、自動テストによる品質担保を可能にするためだ。もしあなたが今、複雑怪奇なPL/Iと格闘しているなら、まずはその`GOTO`を構造化制御(`IF-THEN-ELSE`や`DO-WHILE`)に書き換えることから始めてほしい。
それが、次の10年を支える基幹システムを構築するための、最も確実な第一歩となるはずだ。
—
追伸:もし貴殿のプロジェクトで「コンパイラのバグではないか?」と疑うような不可解な挙動に遭遇したら、まずは`DCL`の宣言順序と`ALIGN`属性を疑え。コンパイラは嘘をつかないが、メモリ配置の仕様は時として、我々を欺くほどに冷酷である。
