bcscanをビルドして動かしてみたら'stack level too deep'と言われた

RubyCocoaの開発ができる状態になったので、下記からbcscan*1のソースを取ってきてビルドしてみた。

とりあえずビルドはできたが、実行してみると下記のようなエラー出てしまった。

[Session started at 2008-09-24 23:25:17 +0900.]
/Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `size': stack level too deep (SystemStackError)
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `count'
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `size'
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `count'
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `size'
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `count'
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `size'
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `count'
	from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/objc/oc_attachments.rb:1723:in `size'
	 ... 6829 levels...
	from /opt/local/lib/ruby/1.8/optparse.rb:1334:in `permute!'
	from /opt/local/lib/ruby/1.8/optparse.rb:1355:in `parse!'
	from /Users/snaka/work/bcscan-0.1/build/Release/bcscan.app/Contents/Resources/command.rb:27:in `command_init'
	from /Users/snaka/work/bcscan-0.1/build/Release/bcscan.app/Contents/Resources/rb_main.rb:21

bcscan はステータス 1 で終了しました。

どうもObject-CのNSArrayの拡張クラスの中でcountメソッドとsizeメソッドがお互いに呼び合ってループしているように見える。とりあえず、ループする原因はoc_attachments.rbに問題がありそうだが、よくわからないので別の切り口から回避方法を探ることにした。

まず、エラーが発生の発端となっている、command.rb を見てみる

    parser.parse!(args)

ここは単にコマンドラインパラメタを解析しているはずの箇所。parserとはOptionParserクラスを指しているはずで、渡される値も単純な Ruby の配列オブジェクトだと思われる。それなのに、Object-CのNSArrayクラスとして処理が行われている点が怪しい。

ということでargsの値を設定している箇所を調べてみる。そうすると以下のように記述されている。

  args = OSX::NSProcessInfo.processInfo.arguments.collect {|arg| arg.to_s}

おそらく、ここで args の値としては Ruby の配列(Array)を期待しているはずだが、実際は Object-CのNSArrayオブジェクトになっちゃてるんじゃないか... と推測。それを検証するため以下のように puts を追加してみた。

  args = OSX::NSProcessInfo.processInfo.arguments.collect {|arg| arg.to_s}
  puts "*** #{args.class}"

で、実行...

*** OSX::NSCFArray

やはり NSArray になっている。これをRubyの配列に戻してあげれば正しく動くはず。以下のよう修正してみた。

  args = OSX::NSProcessInfo.processInfo.arguments.collect {|arg| arg.to_s}
  args = args.to_a

ビンゴでした。たぶん、RubyCocoaの仕様が変ったとかの理由なんだろうな... ということでとりあえずオK

*1:MacBookに内蔵されているカメラ(iSight)を使ってバーコードを読み取るコマンドラインツール