ruby.wasmをブラウザで動かす時require_relativeを相対パスへのfetchに置き換えられる? - @ledsun blog で ruby.wasm ハックアイデアを思いつきました。 実際にやってみます。
packages/npm-packages/ruby-wasm-wasi/src/browser.script.ts に次のようなコードを足すとrequire_relative
関数にモンキーパッチを当てられます。
vm.eval(` require "js" module Kernel def require_relative(relative_feature) JS.global.fetch(relative_feature + '.rb') end end `);
fetchしたは良いものの、ダウンロードしてきたRubyスクリプトを評価する方法がわかりません。
vm.eval
に渡せばいいのですが、Rubyスクリプト内からJavaScript内のvm
変数を参照する方法がわかりません*1。
あるいは、RubyのVMの中で評価できればそれでもいいかもしれません。 モンキーパッチを当てる前のrequire_relativeはどうやって取得したRubyスクリプトを評価しているのでしょうか?
Rubyメソッドの実装を参照するためのいくつかの方法 - BOOK☆WALKER Tech Blog を参考にしてrequire_relative のソースコードを探します。
- Kernel.#require_relative (Ruby 3.1 リファレンスマニュアル)
- module Kernel - RDoc Documentation
- ruby/load.c at 4325e90205aa4cd0ea031df1b5e6334bfd9c7e51 · ruby/ruby · GitHub
rb_require_string -> require_internal -> require_internal -> load_iseq_eval -> rb_parser_load_file て、めっちゃC言語の中をさまよっているからRubyスクリプトから呼べなくないですか?
もしかして単にKernel.evalすればいいですか? では、Rubyスクリプト内fetchで取得したファイルから文字列を取り出しましょう。 と、おもいましたが、RubyスクリプトからJavaScriptのfetch関数をawait付きで呼び出したり、thenにコールバック関数を渡す方法がわかりません。
あれ?JavaScriptのグローバルオブジェクトが取れるのであれば、グローバルオブジェクトに関数を生やせば、Rubyスクリプトから呼び出せますか?
つまり次のようにrb_require_relative
グローバル関数を定義します。
const _global = (window) as any _global.rb_require_relative = (relative_feature) => { fetch(relative_feature + '.rb') .then(response => response.text()) .then(ruby_script => vm.eval(ruby_script)) } vm.eval(` require "js" module Kernel def require_relative(relative_feature) JS.global.rb_require_relative(relative_feature) end end `);
そして次のようなhoge.rbをrequire_relative 'hoge'
して上げれば
p "hoge loaded!"
こうです!