@ledsun blog

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

JS::Object#new PR感想戦

ruby.wasmでJavaScriptのオブジェクトをnewメソッドで初期化する - @ledsun blogで、JS::Object#newを実装しました。 これを Create a JavaScript object with the new method. by ledsun · Pull Request #246 · ruby/ruby.wasm · GitHub にしました。

マージするまでに学びがあったので、感想戦を行って記憶に定着させます。

修正内容

  1. newメソッドを定義
  2. Reflect.constructメソッドを使う
  3. to_jsをデータクラスの責務とする

以上の修正で「JS::Object#newという1行メソッドを実装するだけ」という、やりたい事をまんま実装するだけというシンプルな拡張になりました。

newメソッドを定義する

method_missingの中でメソッド名で分岐していました。

def method_missing(sym, *args, &block)
  if sym == :new
    # new で呼び出されたら、コンストラクタとして呼び出す。
    JS.eval("return #{self.to_construct(args)}")
  elsif self[sym].typeof == "function"
    # 関数として定義されていたら、関数として呼び出す。
    self.call(sym, *args, &block)
  else
    super
  end
end

単にnewメソッドをとして定義すれば良いのでした。

def new(*args)
  JS.eval("return #{self.to_construct(args)}")
end

条件分岐が減ってシンプルになりました。

Reflect.constructメソッドをつかう

JavaScriptのnew構文を作ってインスタンス生成していました。

JS.eval("return 'new URLSearchParams()'")

JavaScriptReflect.construct関数*1をつかうと、new構文と同じことが出来ます。

JS.global[:Reflect].construct(self, args.to_js)

これには2つのメリットがあります。

  1. 関数オブジェクトから関数名をとる処理が不要
  2. 関数名を持たない無名関数からもインスタンスか生成出来る

to_jsの実装

Reflect.constructJavaScriptの配列を引数にとります。 コンストラクタの引数を指定するには、Rubyの配列をJavaScriptに配列に変換する必要があります。

変換用のクラスを作りました。 実装は少し長いので、興味のある人だけリンク先をご覧ください。 https://github.com/ledsun/ruby.wasm/blob/2e24c567f2adddd540832fbc1e00141aa51669d6/ext/js/lib/js.rb#L166-L201

Array#to_jsメソッドを定義することにしました。

class Array
  # Convert Ruby array to JavaScript array
  def to_js
    new_array = JS.eval("return []")
    self.each do |element|
      # NOTE: This method call implicitly convert element to JS object by to_js
      new_array.push element
    end
    new_array
  end
end

各データクラスがJavaScriptのオブジェクトを返す責務果たせば良いのでした。

まとめ

見比べると、なかなか興味深いです。

  • 変更行数はほとんど変わりません。
  • 変更ファイル数は2 → 9で大きく増えています。

ふりかえってみると「既存コードへの理解がすくなく、どこを変更したらいいのかよくわかっていなかった」ように思えます。 おそらく「知っているファイルだけで修正を済ませたい」心理が働いて良そうです。 「大多数の知らないファイルについて調べないで済ませる」省エネ発想だと思います。

必ずしも悪い発想ではないと思います。 未知の物を完全に理解するのは時間がかかります。 やりたい事と動くことを示して、アドバイスをもらう方が早く進められそうです。

仕事でやるなら「事前に相談して設計方針を固めてから進める」方が早いです。 けど、それはそれでハッキングぽくなくて楽しくないですよね。

とりあえず今回修正したファイル群の理解は進みました。 次以降の修正の参考になると期待しています。

*1:JavaScriptに、こんな便利な関数があるなんて知りませんでした。