汎用機の深淵:PL/I `ON ENDFILE` の作法と、移行設計における「罠」
メインフレームの世界では、コードは単なるロジックの羅列ではない。それはCPUのサイクル、メモリの配置、そして何十年もの間、深夜のバッチウィンドウを支えてきた「信頼の系譜」そのものだ。
若手やオープン系出身のアーキテクトがPL/Iを読み解く際、最も直感に反し、かつ最もトラブルの温床となるのが `ON ENDFILE` 条件の制御フローである。今日は、このレガシーの知見を紐解いていく。
—
ON ENDFILE は「goto」ではない、という理解
Javaの `while(reader.readLine() != null)` に慣れた諸君にとって、`ON ENDFILE` は非同期の例外ハンドリングのように見えるかもしれない。しかし、PL/Iの `ON` ユニットは、ブロックの実行コンテキストを一時的にジャックする「割り込み処理」だ。
/i
/ ファイル終了判定の古典的かつ堅牢な実装 /
DCL EOF_FLAG BIT(1) INIT(‘0’B);
ON ENDFILE(INPUT_FILE) EOF_FLAG = ‘1’B;
READ FILE(INPUT_FILE) INTO(INPUT_RECORD);
DO WHILE(EOF_FLAG = ‘0’B);
/
- ここで処理を行う。
- 重要なのは、読み込みをループの最後で行うか、
- 前処理として独立させるかの設計だ。
/
CALL PROCESS_RECORD(INPUT_RECORD);
READ FILE(INPUT_FILE) INTO(INPUT_RECORD);
END;
ここで重要なのは、`ON` ユニット内でのフラグ設定と、メインループへの「復帰」の挙動だ。もしここで `GO TO` を使ってブロックの外へ脱出しようとすれば、スタックの整合性が崩れ、思わぬアベンド(S0C1やS0C4)を招く。`ON` ユニットはあくまで「状態」を書き換え、制御をメインフローに戻すための「安全装置」として使うべきだ。
—
アーキテクトの視点:マイグレーション時の「暗黙の罠」
レガシーマイグレーションにおいて、この構造をJavaやC#へ移植する際、単なる `while` 文への書き換えで満足してはならない。
1. パックデシマル(COMP-3)の符号問題
PL/Iの `FIXED DECIMAL` は、しばしばメインフレームのバイナリデータとして直接I/Oされる。特に、ホスト側のパックデシマル末尾のニブル(符号部)が、マイグレーション先で正しく解釈されないケースがある。EBCDICからASCII/UTF-8への変換時に、符号ビットが反転し、データ値が狂う。このバグはテスト環境のきれいなデータでは顕在化せず、本番の複雑なデータで突然噴出する。
2. 埋め込みSQL(DB2)との親和性
CICS環境下でのバッチ処理において、`ON ENDFILE` と `SQLCODE` のチェックを混在させるのは危険だ。特にカーソルループとファイルI/Oが入れ子になっている場合、`ON` ユニットのスコープが予期せぬ場所まで有効になり、デバッグ困難なメモリエラーを引き起こす。可能な限り `SIGNAL` 文を活用し、条件を局所化することを推奨する。
—
動的メモリとポインタが教える「真実」
PL/Iの真価は、`BASED` 変数と `POINTER` を駆使した動的メモリ操作にある。
/i
DCL RECORD_PTR POINTER;
DCL INPUT_RECORD CHAR(100) BASED(RECORD_PTR);
/
- GET STORAGEでメモリを確保し、
- オフセットを操作してレコードをマッピングする。
- これにより、複数のレコード構造を同じ領域で再利用可能にする。
/
ALLOCATE INPUT_RECORD;
/ … 処理 … /
FREE INPUT_RECORD;
現代の言語ではGC(ガベージコレクション)に頼り切りだが、PL/Iにおいてメモリの生存期間を制御するのはアーキテクトの矜持だ。ここを理解せずにマイグレーションを行うと、Java側で膨大な `Heap` を消費し、結果としてパフォーマンス劣化(GC頻発)を招く。`FREE` を忘れたメモリリークが、メインフレームの堅牢なメモリ管理を模倣しようとする過程で発生するのは、皮肉なことに非常に多い現象だ。
—
ダンプ解析は「会話」である
万が一、`ABEND` が発生したとき、諸君が見るべきは `CEEEDUMP` だ。
コンパイラオプションの `LIST` や `MAP` を駆使し、オフセットとソースコードを照合する。もし `S0C7` が発生したなら、それはデータ形式の不一致か、あるいは初期化されていないポインタへのアクセスだ。
PL/Iは、型チェックが非常に厳格であると同時に、特定の条件下では極めて寛容に(あるいは無慈悲に)メモリを書き換える。最適化オプション(`OPTIMIZE(3)` など)をかけると、コンパイラはレジスタを最大限に活用し、変数の生存範囲をコードから剥ぎ取る。デバッガ上で変数の値が `OPTIMIZED AWAY` と表示されたとき、初めて諸君は「最適化と信頼性のトレードオフ」という、汎用機アーキテクチャの核心に触れることになる。
結びとして
PL/Iからモダン言語への移行は、単なる「コードの変換」ではない。それは、過去数十年のシステムが蓄積してきた「メモリレイアウトの深層心理」を解読し、新しい器へ移し替える儀式だ。
もし諸君が、既存のコードの `ON ENDFILE` を見直す機会があれば、単にループを書き換えるのではなく、その背後にある「データの流れ」と「生存期間」を設計図に落とし込んでほしい。それこそが、世界最高峰のメインフレーム・アーキテクトが歩んできた道であるはずだ。
技術は変われど、堅牢なシステムを構築するための「論理的整合性」という指針は、いつの時代も変わらない。
