日報 2016-07-20

exported form https://nippo.wikihub.io/@_ksss_/20160720141728

おもしろそうなのでこっちに書いてみる。 riloっていうmrubyで動くミニマムエディターを作っている。

https://github.com/ksss/rilo

ワンバイナリ化

mruby-cliについて勘違いしてた。 mruby-cliはmrubyでcliツールを作りたい時に、雛形をいい感じに生成してくれるものっぽい。 mrubyのお作法をある程度省略できるのがメリットっぽい。 特にbuild周りに重点されていて、Dockerfileとかdocker-compose.ymlも用意してくれる。 当然雛形なので気に入らない部分とかは書き換えちゃえばいい。

あと、コード読むときに「ああ、あれね」という分かりをえることができるのも利点かなあ。

mruby-clirakeするとmruby v1.2.0を落としてきてbuildするようになっている。 もちろん手元のmrubyでbuildすることもできる。 依存するライブラリも手元のやつを指定すれば、もしライブラリがバグってた時に直せる。 mruby-cliは雛形生成ツールなので、これに依存しているからbuild_config.rbでなになにみたいなことはない。

もともとmruby-cli無しで四苦八苦して作っていたのが、だいたいこの雛形と一致したので僕にはあんまり意味がなかったっぽい。 (当然Dockerまわりは助かった)

参考リンクからはこの「雛形生成ツール」であることがいまいち伝わらず、 「なんでいるんだろう?なくてもcliツールできるくない?」と思ってた。

というわけでriloもmruby-cliに対応?したのでワンバイナリとしてbuild & 実行可能になった。

自分がよく使うツールを自作する感じは楽しい。

だいたいの仕様をkiloにあわせてるけど、 別に合わせる必要はないので、頃合いを見て参考にせず突っ走っていこう。

参考リンク

http://dojineko.hateblo.jp/entry/2016/02/22/002701 http://blog.kentarok.org/entry/2015/10/27/000522 http://hb.matsumoto-r.jp/entry/2015/10/23/133753

日報 2016-07-22

exported from https://nippo.wikihub.io/@_ksss_/20160722124458

mruby本体に2つバグがあった。

一つめ

Struct.new(:a) do
  def foo
  end
end

とすると、fooメソッドはStruct.newで作られた新しいclassのメソッドになることを期待するが、 なんと、Structclassのメソッドになるというもの。ここでメソッドを定義してしまうと、全部のStruct classの小クラスで共有されちゃう。 しかもselfはちゃんとStruct classの小クラスになっていて気が付きにくく邪悪。

ほんとStructって誰も使ってないんじゃないの……。

これの修正は1行修正でよかった

https://github.com/mruby/mruby/pull/3179/files

二つめ

Struct.new(:foo) {
  def initialize(*)
    self.foo
  end
}.new

がSEGVするというもの。

ここでのinitializeは、新しく作られたStructからのインスタンスclassのinitializeで、 それを上書きしているわけだけども、上書きしないinitializeでself.fooと呼べる準備をしており、 その準備をしないままだと、ありえない参照(StructはArrayとほぼ同じだ)をしてしまって落ちるようだった。

一つめの修正前でもselfは新しいStructのインスタンスclassなので、1つめとは別の問題。

一つめの修正をした上で

https://github.com/mruby/mruby/pull/3178/files

の修正をするとなおるっちゃなおるんだけど、実装が汚い。

なぜ汚いかというと、C言語は関数のスコープを「ファイルで閉じるスコープ」か、「実装内部だけで共有できるスコープ」か、「別のアプリケーションから参照できる公開されたスコープ」を作れる。 mrubyには「実装内部だけで共有できるスコープ」の部分がない(CRubyにはある)ので、 これをわざわざ作るのか、それともpublicなAPIとするしか共有するすべがない。

この辺を考えるのがめんどくさかったので、別の「ファイルで閉じるスコープ」の関数をコピペしてきた。これが微妙だ。

まあCRubyでいうallocにそうとうするやつなので公開してもいいような……でもmrubyではallocメソッドを作ってないから公開しないほうがいいような……。

というわけで、判断はmatzにまかせることにした。 margeされなくても、セグフォなのでいずれ誰かが何とかするでしょう。

日報 2016-07-31

exported form https://nippo.wikihub.io/@_ksss_/20160731140352

mrubyでのforkのバグを直した。

https://github.com/iij/mruby-process/pull/7

引数に初期化されてない変数がブロック引数に渡されているので、 これをmruby側で参照すると死ぬ。

そもそもforkにブロック引数はいらない。

mrubyのC側でブロック引数を無くす方法は2つ

  • mrb_yield(mrb, block, mrb_nil_value())
  • mrb_yield_argv(mrb, block, 0, NULL)

mrb_yieldmrb_yield_argvはほぼ同じ関数だが、ブロック引数の取り方に違いがある。 mrb_yieldはブロック引数を1つだけ指定できる。 mrb_yield_argvは0〜いくらでも指定できる。 関数の中身もほぼ同じ

MRB_API mrb_value
mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
{
  struct RProc *p = mrb_proc_ptr(b);

  return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class);
}

MRB_API mrb_value
mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
{
  struct RProc *p = mrb_proc_ptr(b);

  return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
}

https://github.com/mruby/mruby/blob/eb7422af581edff0dd4b1f8259598677b4d32793/src/vm.c#L657-L671

要約すると、mrb_yieldではどんなにがんばっても引数を一つ与えていることになるので、意味的にはおかしい。 ブロック引数を0個にしたければ、mrb_yield_argv(mrb, block, 0, NULL)を使うほうがヨサゲ。

どうやってこのバグを発見したかというと、単にコンパイルしたらclangがwarningで教えてくれただけだ。

つまりコンパイルさえすれば発見できる状態にあるが、2年近く放置されていたことになる。(ブロック引数なんかつけなきゃ問題ないので)

そしてこんなミミッチイ事を気にするのは、世界で僕ぐらいだったようである。

おしまい。