@ledsun blog

無味の味は佳境に入らざればすなわち知れず

RubyVM.evalAsyncの中でRubyVM.evalAsyncを呼ぶとエラーが起きる

JS::Object.awaitが返らないときがある? - @ledsun blog にて次のような疑問を持ちました。

this.vm.evalAsync(script.ScriptBody)の中でthis.vm.evalAsync(script.ScriptBody)するのがよくないのでしょうか?

素朴にevalAsyncの中でevalAsyncを呼ぶとどうなるのか試してみます。

<html>
  <script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0-2022-12-05-a/dist/browser.script.iife.js"></script>
  <script>
    function main() {
      window.rubyVM.evalAsync(`
        require 'js'
        JS.global[:rubyVM].evalAsync("puts 'a'")
      `)
    }

    // RubyVMの初期化を待ってから、mainを実行します。
    const id = setInterval(() => {
      if (window.rubyVM) {
        clearInterval(id)
        main()
      }
    }, 300)
  </script>
</html>

実行すると

見慣れないエラーが出ます。

vm_call_cfunc: cfp consistency errorRubyのエラーのようです。 Bug #11903: [BUG] vm_call_cfunc - cfp consistency error - Ruby master - Ruby Issue Tracking System に同じエラー文面に関するバグ報告があります。 報告内容は関係ないだろうと思って読んでいません。

Uncaught (in promise) RuntimeError: unreachableはAsyncifyに関わるエラーのようです。 [Asyncify] RuntimeError: unreachable · Issue #15488 · emscripten-core/emscripten · GitHub に同じエラー文面に関するバグ報告があります。

まあ、よくわかりません。

ちなみにどちらかのevalAsyncをevalに返ると動きます。

window.rubyVM.eval(`
  require 'js'
  JS.global[:rubyVM].evalAsync("puts 'a'")
`)

または

window.rubyVM.evalAsync(`
  require 'js'
  JS.global[:rubyVM].eval("puts 'a'")
`)

です。

JS::Object.awaitが返らないときがある? - @ledsun blog のときはこのエラーは起きていません。 evalAsync内でfetchして取得したRubyスクリプトをevalAsyncしてるはずなので、同じ現象がおきるはずだと思っていました。 何かが違うようです。・・・。 RubyJavaScriptを行ったり来たりしているところが、いまいち追いかけられなくて、頭の中でワープさせているんですが・・・正しく想像出来ていなさそうです。

evalAsyncの中はFiberで実装されている*1のですが・・・また、これがコードがどこにどう飛ぶのがかわかりません。

追記

Bug with passing Procs around: "RuntimeError: unreachable" · Issue #34 · ruby/ruby.wasm · GitHub を眺めていたらもう少し短い再現コードが書けることに気がつきました。

<script type="module">
  import { DefaultRubyVM } from 'https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0-2022-12-05-a/dist/browser.esm.js'

  const main = async () => {
    // Fetch and instantiate WebAssembly binary
    const response = await fetch(
      "https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0-2022-12-05-a/dist/ruby+stdlib.wasm"
    );
    const buffer = await response.arrayBuffer();
    const module = await WebAssembly.compile(buffer);
    const { vm } = await DefaultRubyVM(module);
    window.rubyVM = vm;

    vm.evalAsync(`JS.global[:rubyVM].evalAsync("puts 'a'")`)
  };
  main();
</script>