原文はこちら。
The original article was written by Christoph Schobesberger (working in Master Course, Computer Science, Johannes Kepler University in Linz).
https://medium.com/graalvm/integrating-cobol-with-javascript-c2d1ffe00678
GraalVMはJavaScript、Ruby、R、Pythonなど様々な言語をサポートしており、この中にはLLVM bitcode実行ランタイムである Sulong も含まれます。このSulongではLLVM bitcodeにコンパイルされたプログラム、通常はCやC++といったネイティブ言語からコンパイルされたプログラムを実行できます。GnuCOBOLのようなCOBOLのコードをCのコードにコンパイルするコンパイラを使うと、COBOLプログラムをSulongで実行できます。GraalVMの相互運用機能を使えば、COBOLのコードを別のプログラミング言語から呼び出すことができますし、その逆もまた然りです。このような1959年に作られたテクノロジーとGraalVMのような最新のテクノロジーが相乗効果を生み出すのは非常にわくわくさせられます。
このエントリでは相互運用の実現方法を紹介し、ご自身のプログラムで試すことができるよう低レベルの詳細情報を説明します。COBOLでプログラムを書き、一部をJavaScriptに書き換え、その後オリジナルのCOBOLプログラムを実行してJavaScriptモジュールを呼び出し、今度はCOBOL関数を呼び出すことができるようにします。
SHA-3 Library written in Cobol
サンプルアプリケーションとして、GnuCOBOLのWebサイトから入手できるSHA-3のCOBOL実装を使います。実際のアルゴリズムを実行する1 つの共通モジュール KECCAK.cob と、SHA3 の異なるビット幅のためのいくつかのモジュールがあります。TESTSHA3.cob というモジュールはSHA-3実装モジュールを呼び出してテストします。SHA3-256.cob モジュールをJavaScriptで書き換え、これをCOBOLから呼び出します。JavaScriptコードはその後COBOLで記述されたKECCAK.cobモジュールを呼び出す必要があります。

Prerequisites
- まず、GraalVMが必要です(20.2以上)。この例では、Sulongのビットコード実行を完全に仮想化し、ネイティブコードの実行をより確実にするマネージドモードを使用しています。このため、LLVM ToolchainとともにEnterprise Editionが必要で、今回はJDK 8ベースのディストリビューションを使いました。GraalVMは以下のページがら入手できます。
Oracle GraalVM Enterprise Edition
https://www.oracle.com/downloads/graalvm-downloads.html
- さらに、この例をご自身で試したいと思ってらっしゃる場合、変更されたGnuCOBOLのソースコード、SHA-3のサンプル、GnuCOBOLをコンパイルするために必要なライブラリは以下のGitHubリポジトリから入手できます。
Sulong-Cobol
https://github.com/cSchobi/Sulong-Cobol
- ソフトウェアプロジェクトのビルドのためにmakeとhelp2manが必要です。
Setup
最初に、環境変数を設定し、すべてのGraalVMツールを利用できるようにする必要があります。必要なコマンドはsetup.shというスクリプトにまとめてあります。
export GRAALVM_PATH=/path/to/graalvm-ee-java8-20.2.0
# export path for lli
export PATH=$GRAALVM_PATH/bin:$PATH
export LLVM_TOOLCHAIN=$(lli --llvm.managed --print-toolchain-path)
# set path for gcc
export PATH=$LLVM_TOOLCHAIN:$PATH
# path to graalvm managed libraries
export GRAALVM_LIBRARIES_PATH=$GRAALVM_PATH/jre/languages/llvm/managed/lib
# path to directory containing libraries that are compiled for Sulong managed mode
export MANAGED_LIBRARIES_PATH=$(pwd)/bitcode-managed
# include cobc in search path
export PATH=$MANAGED_LIBRARIES_PATH/bin:$PATH
環境変数 GRAALVM_PATH
をご自身の環境に合わせて設定する必要があります。 source setup.sh
を実行後、the command which lli
と which gcc
を実行して、GraalVMのコマンドに向いていることを確認してください。
さらに、GRAALVM_LIBRARIES_PATH
と MANAGED_LIBRARIES_PATH
という2個の環境変数は後ほど使います。最後に、GnuCOBOLの実行ファイルである cobc
をMANAGED_LIBRARIES_PATH
で指定したディレクトリにインストールし、PATH
を通しておきます。
環境変数の設定が済んだら、GraalVM用にGnuCOBOLをマネージドモードでコンパイルできます。GnuCOBOLでは、数学関数のGMP
またはMPIR
、ファイル管理のためにBerkeley DB (libdb
)、VBISAM
またはDISAM
のいずれかを必要とします。今回はGMP
とVBISAM
を使用します。
GMP のビルドのため、GitHubリポジトリからクローンしたディレクトリにある gmp ディレクトリに移動し、以下のコマンドを実行します。
mkdir build-gmp-bitcode-managed
cd build-gmp-bitcode-managed
../configure --prefix=${MANAGED_LIBRARIES_PATH} --disable-assembly \
--host=x86_64-unknown-linux
make
make install
詳細は以下の記事をご覧ください。
Compiling Native Projects via the GraalVM LLVM Toolchain
https://medium.com/graalvm/graalvm-llvm-toolchain-f606f995bf
https://logico-jp.io/2019/12/25/compiling-native-projects-via-the-graalvm-llvm-toolchain/
VBISAM のビルドのため、vbisamディレクトリに移動して以下のように実行します。
./configure --prefix=${MANAGED_LIBRARIES_PATH}
make
make install
これでgnucobolディレクトリに移動して、GnuCOBOLをコンパイルできるようになりました。
./configure CFLAGS="-I${MANAGED_LIBRARIES_PATH}/include"
LDFLAGS="-L${MANAGED_LIBRARIES_PATH}/lib"
--disable-nls
--with-vbisam --prefix=${MANAGED_LIBRARIES_PATH}
make
make install
which cobc
というコマンドは、マネージドライブラリのあるディレクトリに配置された先ほどインストールした実行ファイルをさしているはずです。cobc -i
を実行してすべてが正しく設定されていることを確認することもできます。
Rewriting Cobol in Javascript
すべて設定が完了したので、サンプルを実行してみましょう。SHA3–256.cobというCOBOLモジュールをJavaScriptで書き直したいのですが、まず最初に、TESTSHA3–256.cobというモジュールからこのモジュールを呼び出すように変更する必要があります。このモジュールにはTESTSHA3.cob
と同じコードがありますが、SHA3–256関数だけを呼び出しているからです。
SHA3–256関数は以下のように呼ばれます。
CALL "SHA3-256" USING WS-INPUT | |
WS-INPUT-BYTE-LEN | |
WS-SHA3-256-OUTPUT | |
END-CALL |
まず、Sulongのpolyglot機能を使ってJavaScript関数へのポインタを取得する必要があります。このためにworking-storageセクションに3個の変数を追加で宣言し、プロシージャ部でpolyglot_eval_file
を呼んでいます。
*> interoperability fields | |
01 WS-SHA3-256-JS-POINTER PROGRAM-POINTER. | |
01 WS-JS PIC X(3). | |
01 WS-SHA3-256-JS-FILENAME PIC X(50). | |
MOVE Z'js' TO WS-JS. | |
MOVE Z'SHA3-256.js' TO WS-SHA3-256-JS-FILENAME. | |
*> get javascript function | |
CALL STATIC "polyglot_eval_file" using | |
WS-JS | |
WS-SHA3-256-JS-FILENAME | |
returning WS-SHA3-256-JS-POINTER | |
END-CALL |
polyglot_eval_file
はCの関数でありCはNullで終端する文字列を期待しているため、Z’…’
を使ってNull終端を追加しています。このようにこの関数ポインタを使ってJavaScript関数を呼び出すことができるようになりました。
CALL WS-SHA3-256-JS-POINTER USING WS-INPUT | |
WS-INPUT-BYTE-LEN | |
WS-SHA3-256-OUTPUT | |
END-CALL |
JavaScriptのコードは以下のような感じです。
// load C Code | |
var cobol = Polyglot.evalFile("llvm", "KECCAK.so"); | |
(function SHA3_256(sha_input, sha_input_length, sha_output) { | |
cobol.KECCAK__Wrapper__struct({ | |
LNK_KECCAK_RATE: 1088, | |
LNK_KECCAK_CAPACITY: 512, | |
LNK_KECCAK_INPUT: sha_input, | |
LNK_KECCAK_INPUT_BYTE_LEN: sha_input_length, | |
LNK_KECCAK_DELIMITED_SUFFIX: 0x06, | |
LNK_KECCAK_OUTPUT: sha_output, | |
LNK_KECCAK_OUTPUT_BYTE_LEN: 32 | |
}); | |
// GnuCobol expects a return value | |
return 0; | |
}) |
基本的に、まずKECCAK
関数を含む共有ライブラリを評価する必要がありますが、生成されたCのコードがSulongに他意強いて十分な型情報を提供していないため、残念ながらKECCAK
関数を直接呼び出すことはできません。今回はKECCAK関数のインターフェースを調整したりライブラリ全体をリファクタリングせずに、ラッパー関数を作成してそれを呼ぶことにします。今回のGnuCOBOLのソースコードをそのように変更して、このラッパー用に必要な型情報を自動的に生成できます。
このラッパー関数はKECCAK関数のそれぞれのパラメータに対応するメンバー変数を持つJavaScriptオブジェクトを受け取ります。以下はそのラッパー関数です。
IDENTIFICATION DIVISION. | |
PROGRAM-ID. KECCAK-Wrapper-struct. | |
ENVIRONMENT DIVISION. | |
DATA DIVISION. | |
WORKING-STORAGE SECTION. | |
LINKAGE SECTION. | |
01 LNK-KECCAK. | |
02 LNK-KECCAK-RATE BINARY-LONG UNSIGNED. | |
02 LNK-KECCAK-CAPACITY BINARY-LONG UNSIGNED. | |
02 LNK-KECCAK-INPUT POINTER. | |
02 LNK-KECCAK-INPUT-BYTE-LEN POINTER. | |
02 LNK-KECCAK-DELIMITED-SUFFIX BINARY-CHAR UNSIGNED. | |
02 LNK-KECCAK-OUTPUT POINTER. | |
02 LNK-KECCAK-OUTPUT-BYTE-LEN BINARY-DOUBLE UNSIGNED. | |
PROCEDURE DIVISION USING LNK-KECCAK. | |
CALL "KECCAK" USING | |
LNK-KECCAK-RATE | |
LNK-KECCAK-CAPACITY | |
BY VALUE LNK-KECCAK-INPUT | |
BY VALUE LNK-KECCAK-INPUT-BYTE-LEN | |
BY REFERENCE LNK-KECCAK-DELIMITED-SUFFIX | |
BY VALUE LNK-KECCAK-OUTPUT | |
BY REFERENCE LNK-KECCAK-OUTPUT-BYTE-LEN | |
END-CALL | |
EXIT. | |
END PROGRAM KECCAK-Wrapper-struct. |
このラッパー関数はレコードとして引数を取り、レコードのメンバーを使って直接KECCAK
関数を呼び出します。COBOLから受け取る引数は参照渡しなので、ポインタとして引数を取得します。したがって、KECCAK
関数を呼び出すとき、これらのポインタへの参照ではなく、直接これらのポインタを渡す必要があります。したがって、これらのポインタは値渡しです。
レコードを使っている理由と、裏で起こっていることの説明は、Technical Detailsの章で説明します。
Compiling the program
ようやく実際にこのプログラムをビルドする方法をご覧いただけるようになりました。ソースコードにはmake TESTSHA3–256
を実行してプログラムをビルド可能なmakefileが含まれています。必要なmakefileの行を見ていきましょう。
COBCOPTS = -free -Q "-Wl,-rpath -Wl,$(MANAGED_LIBRARIES_PATH)/lib -Wl,-rpath -Wl,$(GRAALVM_LIBRARIES_PATH)" | |
# create dynamic library that can by loaded by Sulong | |
KECCAK.so: KECCAK.cob | |
cobc $(COBCOPTS) -m -fstatic-call -fno-gen-c-decl-static-call -G KECCAK-Wrapper-struct -lpolyglot-mock KECCAK.cob | |
TESTSHA3-256: TESTSHA3-256.cob KECCAK.so | |
cobc $(COBCOPTS) -x TESTSHA3-256.cob -lpolyglot-mock |
変数 COBCOPTS
はcobc
用の引数を設定します。引数-free
はGnuCOBOLに対しCOBOLのソースコードとしてフリーフォーマットを使うように指示します。引数-Q
の後にCコンパイラのlinkフェーズに渡される引数が続きます。これを使ってマネージドライブラリやGraalVMのライブラリへのPATHをハードコードしています。
KECCAK.cob
というモジュールを動的ロード可能なモジュールにコンパイルします。(ver 20.2現在)Sulongマネージドコードではsyscallのdlopen
とdlsym
が未サポートなので、引数-fstatic-call
が必要です(“How are COBOL CALLs compiled?”の章をご覧ください)。引数-Gの後には、COBOLレコードである引数の型情報を生成する関数名が続きます。この機能は、GnuCOBOLの標準ではなく、このプロジェクトに追加されたものであることに注意してください。polyglot関数を何度も宣言しないように、C宣言を生成しないようにGnuCOBOLに指示しています。これは-fno-gen-c-decl-static-call
を使うとエラーになるためです。
TESTSHA3–256
というモジュールを引数-x
で実行ファイルにコンパイルします。この実行ファイルにはbitcodeも含まれるため、GraalVMで実行できます。
以下のコマンドで実行できます。
lli --polyglot --llvm.managed TESTSHA3–256
Technical Details
Needed modifications of GnuCOBOL and libraries
上記の例では、GnuCOBOLの修正版を使用しています。公式版を使わなかった理由は、Sulong管理モードではJVMがsyscallを処理するとはいえ、すべてのsyscallが現在実装されているわけではないためです。
幸いにも、GnuCOBOLはconfigure.acというファイルを使って、どのsyscallが利用可能かをチェックし、利用可能なsyscallだけを使うようにプログラムを設定します。したがって、このファイルからサポートされていないsyscallを除き、Sulong管理モードで動作するGnuCOBOLバージョンを用意できます。今回の場合、シグナルハンドラとfcntl
、readlink
、realpath
というsyscallを削除すれば十分でした。さらに、GnuCOBOLは、少なくとも1つのポイントで引数が少なすぎる関数を呼び出すことで、Cの未定義の動作を利用しています。渡された引数だけが必要なので、これはうまくいきますが、Sulongはこの点ではより厳しく、これを許しません。問題のある呼び出しは gmp
ライブラリのクリーンアップを行うだけなので、この呼び出しは現在のところ単純にコメントアウトされています。
同様に、vbisam
というライブラリはあるポイントでfcntl
を使いますが、この呼び出しも除いています。
さらに、前述の通り、Sulongとの相互運用性向上のためにGnuCOBOLのソースコードに手が入っています。
C Code generated by GnuCOBOL
COBOLコードと直接対話するのではなく、GnuCOBOLが生成したCのコードと対話するため、どのようにパラメータを渡しているのかを確認するために生成されたインターフェースをチェックする必要があります。
KECCAK関数には以下のCのインターフェースがあります。
int KECCAK (cob_u8_t *, cob_u8_t *, cob_u8_t *, cob_u8_t *, cob_u8_t *, cob_u8_t *, cob_u8_t *);
ご覧になるとわかる通り、すべてのパラメータをunsigned char型へのポインタとして渡しています。Sulongはこのインターフェースを使用して型情報を抽出しますが、このインターフェースは有用な情報をもたらすことはありません。
理想的には、Sulongが必要とするすべての情報を抽出できるインターフェイスが欲しいところです。これを行うための一つの可能な方法は、Cの構造体と非常に似ているCOBOLレコードを使用することです。GnuCOBOLのレコードは、連続したバイト配列として格納され、メンバ変数へのアクセスは、メモリブロックのベースアドレスへのオフセットを持つポインタアクセスに分解します。Sulongは構造体宣言から型情報を抽出できるので、COBOLレコードのように振る舞うC構造体を生成すればよいのです。GnuCOBOLはオープンソースなので、生成されたソースコードを修正して実現するようにできます。
For the wrapper function that takes a COBOLレコードを引数として取るラッパー関数のため、以下のようなCの構造体を生成します。
struct __attribute__((packed)) KECCAK__Wrapper__struct_LNK_KECCAK { | |
unsigned int LNK_KECCAK_RATE; | |
unsigned int LNK_KECCAK_CAPACITY; | |
void *LNK_KECCAK_INPUT; | |
unsigned long LNK_KECCAK_INPUT_BYTE_LEN; | |
void *LNK_KECCAK_DELIMITED_SUFFIX; | |
void *LNK_KECCAK_OUTPUT; | |
unsigned long LNK_KECCAK_OUTPUT_BYTE_LEN; | |
}; | |
POLYGLOT_DECLARE_STRUCT(KECCAK__Wrapper__struct_LNK_KECCAK) |
Cコンパイラがメンバ変数を整列させないように、packed属性を追加する必要があります。構造体メンバー名はCOBOLレコード内の項目名と同じですが、ただし例外として、名前をC標準と互換性のあるものにしておく必要があります(例えば、文字’-‘は’_’に置き換えられます)。ここまでできれば、あとはラッパーのインターフェースを変更するだけです。
int KECCAK__Wrapper__struct (struct KECCAK__Wrapper__struct_LNK_KECCAK *);
これらの変更でSulongがすべての型情報を抽出できます。
Alternative way of extracting type information
C構造体の自動生成がコード記述量が最小になるソリューションですが、C構造体を生成せずに関数を呼び出すこともできます。ただしこの場合、型情報を手動でSulongに伝える必要があります。以下のようなKECCAKのラッパー関数を書けばいいのです。
IDENTIFICATION DIVISION. | |
PROGRAM-ID. KECCAK-Wrapper. | |
ENVIRONMENT DIVISION. | |
DATA DIVISION. | |
WORKING-STORAGE SECTION. | |
01 WS-KECCAK-RATE BINARY-LONG UNSIGNED. | |
01 WS-KECCAK-CAPACITY BINARY-LONG UNSIGNED. | |
01 WS-KECCAK-DELIMITED-SUFFIX BINARY-CHAR UNSIGNED. | |
01 WS-KECCAK-OUTPUT-BYTE-LEN BINARY-DOUBLE UNSIGNED. | |
LINKAGE SECTION. | |
01 LNK-KECCAK-RATE-POLYGLOT POINTER. | |
01 LNK-KECCAK-CAPACITY-POLYGLOT POINTER. | |
01 LNK-KECCAK-INPUT PIC X. | |
01 LNK-KECCAK-INPUT-BYTE-LEN BINARY-DOUBLE UNSIGNED. | |
01 LNK-KECCAK-DELIMITED-SUFFIX-POLYGLOT POINTER. | |
01 LNK-KECCAK-OUTPUT PIC X. | |
01 LNK-KECCAK-OUTPUT-BYTE-LEN-POLYGLOT POINTER. | |
PROCEDURE DIVISION USING LNK-KECCAK-RATE-POLYGLOT | |
LNK-KECCAK-CAPACITY-POLYGLOT | |
LNK-KECCAK-INPUT | |
LNK-KECCAK-INPUT-BYTE-LEN | |
LNK-KECCAK-DELIMITED-SUFFIX-POLYGLOT | |
LNK-KECCAK-OUTPUT | |
LNK-KECCAK-OUTPUT-BYTE-LEN-POLYGLOT. | |
CALL "polyglot_as_i32" USING | |
LNK-KECCAK-RATE-POLYGLOT | |
returning WS-KECCAK-RATE | |
END-CALL | |
CALL "polyglot_as_i32" USING | |
LNK-KECCAK-CAPACITY-POLYGLOT | |
returning WS-KECCAK-CAPACITY | |
END-CALL | |
CALL "polyglot_as_i8" USING | |
LNK-KECCAK-DELIMITED-SUFFIX-POLYGLOT | |
returning WS-KECCAK-DELIMITED-SUFFIX | |
END-CALL | |
CALL "polyglot_as_i64" USING | |
LNK-KECCAK-OUTPUT-BYTE-LEN-POLYGLOT | |
returning WS-KECCAK-OUTPUT-BYTE-LEN | |
END-CALL | |
CALL "KECCAK" USING | |
WS-KECCAK-RATE | |
WS-KECCAK-CAPACITY | |
LNK-KECCAK-INPUT | |
LNK-KECCAK-INPUT-BYTE-LEN | |
WS-KECCAK-DELIMITED-SUFFIX | |
LNK-KECCAK-OUTPUT | |
WS-KECCAK-OUTPUT-BYTE-LEN | |
END-CALL | |
EXIT. | |
END PROGRAM KECCAK-Wrapper. |
ここでは、polyglot_as_
関数を呼び出して、Sulongに引数の解釈方法を伝える必要がありますが、残念ながら、GnuCOBOLの奇妙な点とSulongのバグのために、この解決策は現在のところ動作しません。ここでの奇妙な部分とは、関数を呼び出す前にJavaScriptでパラメータの数を設定しなければならないということです。その理由は、GnuCOBOLは各呼び出しの前にグローバル変数にパラメータの数を保存しているからです。グローバル変数はJavaScriptの関数を呼び出しにより、値3を持ち、ラッパー関数は7個の引数を取るため、GnuCOBOLは最後の4つの引数を無視してしまうことになります。グローバル変数を設定する、対応するCのコードは、GnuCOBOLによって自動的に生成されます(How are COBOL CALLs compiled?を参照)。
しかし、JavaScriptからCOBOL関数を呼び出すため、この変数を自分で設定しなければなりませんが、これは以下により実現できます。
- パラメータの数を設定できるGnuCOBOLライブラリ
libcob
に関数を追加 - ライブラリのパスを指定して
Polyglot.evalFile
を呼び出してJavaScriptでライブラリlibcob
をロード - 最後に引数7を指定してその関数を呼び出す
しかし、Sulongのバグにより、ライブラリが2度ロードされるため、グローバル変数にJavaScriiptからアクセスできません。この問題は来る20.3リリースで修正されます。
余談ですが、1つのレコード引数だけを使用するラッパー関数でも、パラメーターの数を1に設定すべきですが、実際にはそうする必要はありません。というのも、この変数の値はJavaScript関数の呼び出しから3になっており、この変数が実際のパラメータ数よりも大きな値を持っていても何の問題もないからです。
Call by value vs call by reference
COBOLパラメータは、値または参照で渡すことができ、後者がデフォルトです。JavaScriptからCOBOL関数を呼び出すとき、いくつかの引数を値で渡すことができます。これを行うと、インターフェースは次のようになります。
int KECCAK__Wrapper (cob_s32_t, cob_s32_t, cob_u8_t *, cob_u8_t *, cob_u8_t *, cob_u8_t *, cob_s32_t);
このインターフェースは少しよくなっていますが、理想的ではありません。まず、GnuCOBOLが警告を発します。
KECCAK.cob:78: warning: handling of parameters passed BY VALUE is unfinished; implementation is likely to be changed
implementationKECCAK.cob:79: warning: handling of parameters passed BY VALUE is unfinished; implementation is likely to be changed
implementationKECCAK.cob:82: warning: handling of parameters passed BY VALUE is unfinished; implementation is likely to be changed
KECCAK.cob:84: warning: handling of parameters passed BY VALUE is unfinished; implementation is likely to be changed
続いて、インターフェースの最後のパラメータは32ビットしかありませんが、BINARY-DOUBLEは64ビット必要であることに着目してください。そのため、この機能を利用することはありません。
How are COBOL CALLs compiled?
COBOL CALLはデフォルトで動的に行われます。以下のコードは、関数KECCAKへの動的呼び出しです。
cob_procedure_params[0] = &f_8; | |
cob_procedure_params[1] = &f_9; | |
cob_procedure_params[2] = COB_SET_DATA (f_12, b_12); | |
cob_procedure_params[3] = COB_SET_DATA (f_13, b_13); | |
cob_procedure_params[4] = &f_10; | |
cob_procedure_params[5] = COB_SET_DATA (f_14, b_14); | |
cob_procedure_params[6] = &f_11; | |
cob_glob_ptr->cob_call_params = 7; | |
cob_glob_ptr->cob_stmt_exception = 0; | |
if (unlikely((cob_glob_ptr->cob_exception_code & 0x0b00) == 0x0b00)) cob_glob_ptr->cob_exception_code = 0; | |
if (unlikely(call_KECCAK.funcvoid == NULL || cob_glob_ptr->cob_physical_cancel)) | |
{ | |
call_KECCAK.funcvoid = cob_resolve_cobol ("KECCAK", 0, 1); | |
} | |
b_2 = ((int (*)(void *, void *, void *, void *, void *, void *, void *))call_KECCAK.funcint) (b_8, b_9, b_12, b_13, b_10, b_14, b_11); |
実際の呼び出しの前に、各変数の情報を含む内部データ構造への参照で配列が埋められます。この配列への参照は、呼び出し元がアクセスできるグローバル変数に格納されます。別のプログラミング言語で開始し、生成されたC関数を呼び出すと、GnuCOBOLは、GnuCOBOLがアクティブなCOBOLモジュールを追跡するため、この配列が設定されていないことを認識します。しかし、上記の例では、COBOLからJavaScriptに移行し、COBOLに戻りました。このため、GnuCOBOLは、この配列と変数cob_call_params
が適切に設定されていることを期待します。例えばPIC X ANY LENGTH
型の引数の長さ情報を抽出するために、呼び出し元はこの配列を使用できます。
関数 cob_resolve_cobol
は実行ファイルと、同一ディレクトリに存在する、dlopen
を使って動的にロード可能なモジュールの中で内でKECCAK
シンボルを探し、dlsym
で関数をロードします。
静的callはcob_resolve_cobol
と次の関数呼び出しをこのステートメントに置き換えます。
b_2 = KECCAK (b_8, b_9, b_12, b_13, b_10, b_14, b_11);
Conclusion
この記事では、SulongとGnuCOBOLコンパイラを使用して、GraalVMの上でCOBOLコードを実行する方法を紹介しました。COBOLのサンプルプログラムの機能の一部をJavaScriptで実装し、GraalVMの相互運用性を利用して、COBOLから他のCOBOL関数を呼び出す箇所を、JavaScriptの実装に一部分だけ置き換えて元のプログラムを実行しました。
GnuCOBOLをお使いであれば、是非試していただき、その体験をお知らせください。
また、このアプローチはCのコードにコンパイルするコンパイラを持つ任意の言語に対して拡張可能です。LLVMを直接使う他の言語の場合、ずっと簡単でしょう。もし何かアイデアがあれば、GitHubやTwitterで議論するなどしてご連絡ください。、手を差し伸べてください。COBOLアプリケーションをGraalVM Enterpriseで実行する可能性を探りたい方は、こちらまでご連絡ください。
Graal discussions@GitHub
https://github.com/oracle/graal/discussions
Twitter
https://twitter.com/graalvm
GraalVM Enterprise上でCOBOLアプリケーションを実行する可能性を探りたい方は、こちらまでご連絡ください。