以前iPod touchをJailbreakした際にRubyをインストールしたものの、irbは起動しないし添付ライブラリを読みこんだだけで落ちてしまって、全く使いものにならなかった。その原因が気になっていたので今日はそいつを追っかけてみた。てっきりarm-darwin版のRubyが何かバグを抱えているもんだとばかり思って愚痴ってたのだけども、結論から先に言うとどうもそうではないようだ。で、タイトルのようなことになるのだけども。
とりあえずエラーを吐いてる箇所を探す
エラーを吐いてる、と言うのは正しくない。どのライブラリでも関係なく、requireした時点で[BUG]とだけ言ってAbort trapしてしまうツンツンぶりで、まったくとりあってもらえないというのが現状。数は多くないけど、日本でもarm-darwin版rubyに泣かされてる人は何人かいるみたいだ。
っても、インタープリタのコードもライブラリのコードも読んだことないし、arm-darwinのバイナリを作る開発環境もないしで、インタープリタのバグだったりしたらお手上げだったりするんだけど。まぁ、とりあえず小さそうなところで、URIライブラリを選んでみる。
bash-2.3# ruby -e "require 'uri'" /opt/iphone/lib/ruby/1.8/uri/generic.rb:1121 : [BUG] terminated node (0x21294c) ruby 1.8.6 (2007-03-13) [arm-darwin] Abort trap
ぐ。ここまでは既に何度も経験したので、この程度のツンツン加減じゃへこたれないからね。しかしgeneric.rbの時点で落ちるとはどういうことだ。1121行目に一体何があるのか見てみるべくuri/generic.rbを開いてみる。
1106 def inspect 1107 sprintf("#<%s:%#0x URL:%s>", self.class.to_s, self.object_id, self.to_s) 1108 end 1109 1110 def coerce(oth) 1111 case oth 1112 when String 1113 oth = URI.parse(oth) 1114 else 1115 super 1116 end 1117 1118 return oth, self 1119 end 1120 end 1121 end
endやん。さらっと見てみたけど当然のことながらおかしなところは何もない。他のRubyライブラリか、あるいはもっとローレベルな機能に依存してるとこはないかとも思ったけど、どう見てもごく普通のRubyコード。一応、まさかsprintfが?とか裏切りのcase式か?とか一通り自分で書いて試してみたけど、別に問題なく通る。というか、ここModuleやClassの定義の箇所だし、そもそも実行すらされてないはず。何なんだ一体。
行けるとこまで行かせてみる
何箇所かコメントアウトしてみたりしたら、一応generic.rbは読み込めたりするんだけど、今度は他のライブラリで止まるし、ひっかかる箇所を見比べても共通点が見えてこない。仕方ないのでライブラリのソースにdebug printを仕掛けつつ、上から順番に地道に読みすすめていくことにする。
まずはuri.rbからrequireしてるものをgeneric.rb以外全部コメントアウトして、generic.rbの方では__END__で後ろの処理を無効にして実行してみると、その段階では問題なく読み込める。徐々に読み込む範囲を広げていったら、ついに「そこから先は必ずAbort trapする」行を見つけた。
914 def route_from(oth) 915 # you can modify `rel', but can not `oth'. 916 begin 917 oth, rel = route_from0(oth) 918 rescue 919 raise $!.class, $!.message 920 end 921 if oth == rel 922 return rel 923 end 924 925 rel.set_path(route_from_path(oth.path, self.path)) 926 if rel.path == './' && self.query 927 # "./?foo" -> "?foo" 928 rel.set_path('') 929 end 930 931 return rel 932 end 933 934 alias - route_from
これの925行目の後、return relより前に何かの式を入れるとそこで必ずAbort trapする。ついに、ついに見つけたぜこの野郎。
…待て。うん、この箇所も全然おかしくない。何の変哲もないif文。試しに色々なif文を書いてみる。全部ダメ。unlessもwhileもbegin-rescueもダメ。putsは…ダメ。じゃあ、1。あ、これは通るのか。1+1は…な、なんだって、これも通らないだと?…なんか、すごく嫌な予感がする。手に負えないバグを見つけるよりもよっぽど嫌な予感が。
さて。どうも926行目に書いてあることが原因じゃないようだ。ふむ。じゃあ、コードの順番を入れかえてみたり、前後のコードを減らしたり増やしたりしてみたらどうだろう。
…あはは。コードを減らすと通るんだ。それも、どの箇所かに関係なく。
vim*1を閉じて、おもむろにtop、と打ちこんでみた。うふふふふふ。そういうこと。そういうことなんだ。
メモリが全然なかった。なんでか知らんがhttpd*2起動してるし。誰だそんなふざけたものをiPodに入れたやつは。俺か。俺だよなぁ。
100MBのメモリでできること
結局、iPod touchがあまりに貧弱なため、インタープリタがrequireしたコードを解析中にメモリを使い切ってしまい、どうしようもなくなって[BUG]と断末魔を残して死んでたんだろう、ということだった。だから、コードをあれこれいじってたときに、条件分岐が複雑なコードやオブジェクトを余計に生成するコードが即座に息の根を止めてたんだな。ちょっと考えればわかりそうなもんで、多分携帯電話とかで開発してる人だったら、そんなリソース馬鹿食いしそうなRubyなんかがモバイル端末でまともに動くわけないじゃん、って言いそうだ。あるいは、いやいや、数年落ちのビンテージ環境でもRuby自体は動くわい、それはiPod touchが貧弱なんじゃい、っていう声も違う方から聞こえてきそう。
でもさ。iPhone/iPod touchが携帯電話とも骨董PCとも違うのは、あれは高機能なGUI環境なんだってことなんだよな。サブセットとは言えdarwinが、いやMacOSXが載ってるわけで、だからこそモバイル端末にあるまじき過剰な期待をしてしまうし、(皮肉にも)それが故に骨董PCでもできるようなことが出来なかったりする。確かにiPod touchのインターフェースレベルの層を排してdarwinだけにすれば、Rubyくらいは平気で動くと思うけど、それって何の意味があるんだろう。かと言ってあんなにスムーズに動くUIがあって、音楽・動画が再生できて、ネットワークにも繋がるのに、ハードの制約でいじれる部分が少ないというのも切ない話。
やっぱりObjective-Cで書いて最適化しまくってなんぼって世界か、あるいはWidget程度の小さなアプリがせいぜいってところなのか。Cocoaは好きなんでそれはそれでいいんだけどさ。折角RubyやPerl*3があるのにまともに使えないのは残念過ぎる。まぁ、最初からAppleが言うようにWebアプリで作ればいいじゃんって話だが*4。