@ledsun blog

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

ruby.wasmクイズ

次のスクリプトはエラーになるでしょうか?

<html>
  <script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0/dist/browser.script.iife.js"></script>
  <script type="text/ruby">
    require "js"
    JS::global.send(:require, 'erb')

    erb = ERB.new("Hello <%= val %>!")
    puts erb.result_with_hash val: "world"
  </script>
</html>

ヒント。次のスクリプトはエラーになりません。

<html>
  <script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0/dist/browser.script.iife.js"></script>
  <script type="text/ruby">
    require "erb"

    erb = ERB.new("Hello <%= val %>!")
    puts erb.result_with_hash val: "world"
  </script>
</html>

ヒント2。JS::globalRubyのオブジェクトです。 つまりKernelを継承しています。

ヒント3

irb(main):002:0> 1.send(:require, 'erb')
=> true

つぎのようなコードを書いたら無限ループしました。というお話でした。

  1. JavaScriptwindow.js_require_relativeって関数を定義
  2. RubyKernel#js_require_relativeって関数を定義
  3. 後者からJS.global.js_require_relativeを呼んだ
<html>
  <script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0/dist/browser.script.iife.js"></script>
  <script>
    window.js_require_relative = function() {
      console.log("called")
    }
  </script>
  <script type="text/ruby">
    require "js"

    module Kernel
     def js_require_relative
       JS::global.js_require_relative()
     end
    end

    js_require_relative
  </script>
</html>

を実行すると

SystemStackErrorが発生します。

JS:globalJavaScript側のメソッド検索はmethod_missingで行っています。

https://github.com/ruby/ruby.wasm/blob/802ecc5047ac9ee7270b58e7f11cea87b8172f36/ext/js/lib/js.rb#L84-L90

  def method_missing(sym, *args, &block)
    if self[sym].typeof == "function"
      self.call(sym, *args, &block)
    else
      super
    end
  end

Kernel#js_require_relativeが定義されているため、method_missingが呼び出されていないのだと思います。 次のようにKernel#js_require_relativeをprivateメソッドにします。

<html>
  <script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0/dist/browser.script.iife.js"></script>
  <script>
    window.js_require_relative = function() {
      console.log("called")
    }
  </script>
  <script type="text/ruby">
    require "js"

    module Kernel
     private def js_require_relative
       JS::global.js_require_relative()
     end
    end

    js_require_relative
  </script>
</html>

するとJS::global.js_require_relative()NoMethodErrorが発生し、無事にwindow.js_require_relativeが呼び出されます。

そもそも

  • 同名してうれしいのか?
  • windowオブジェクトに直接関数を増やしたらダメだろ

など、前提条件に疑問はあります。