以前の記事でWebAssemblyの検証をした時に、ccallによる関数呼び出しのタイミングで RuntimeError: memory access out of bounds
が出てうまく動作してくれなかった事がありました。
その時は何だかよく分からず、main関数実行後にコールバックさせたタイミングでccallしたら正常に動作したので、それで良しとしていましたが、理由が分かりましたのでメモしておきます。
まずは、キャッシュのフォルダにあるトランスパイアされたC言語の main 関数を見てみます。
何か NimMain という関数を呼んでいます。
こちらを調べたところ、中でNimのメモリマネージャーの初期化などをおこなっているみたいです。
なので、Nimでメモリアクセスが発生するような動作がある場合は事前に NimMai を呼んで初期化しておかなければならないという事ですね。
逆に言えば NimMain さえ一度通してしまえば、必ずしも main 関数は呼ぶ必要は無いという事になります。
これは、WebAssemblyに限った事ではなく例えばWindows の DLL とかスタートアップの無いプログラム全般に言えることで、英語ですがこちらのドキュメントが参考になりました。
https://nim-lang.org/docs/backends.html#interfacing-backend-code-calling-nim
それを踏まえて、前回のサンプルをこのように直しました。
main実行時にJavaScriptにコールバックする処理は削除しました。
そして、func01 に NimMainを呼出す処理を追加しています。
複数回呼び出されることを防止するため、一度呼び出したらフラグを立てて再度呼び出されないようにしています。
これで大分スッキリとしたコードになりました。
コンパイルオプションはこんな感じ
前回からの変更点は、main関数はもう必要ないので、noMain = "on"を追加して、main 関数が生成されないようにしています。
あと、前回は、色々と不要なオプションもあったので今回それは削っています。
起動用のHTMLです
JavaScriptのコールバック先の関数が無くなり、普通に onRuntimeInitialized で、Emscripten の初期化が終わったタイミングで Nim の関数を呼出すようにしています。
実行結果は前と変わらず。
今回使用したコードはこちらに格納しています。
https://github.com/karasu-jp-com/nim_test/tree/master/nim_test04
一括ダウンロードはこちら