ObjectSpaceダンプ

投稿者 nanki 2011-02-17 14:03:00 GMT
def top_objects(num=20)
  ObjectSpace.each_object.inject(Hash.new{0}){|h,o|h[o.class]+=1;h}.sort_by{|k,v|-v}.first(num).each{|k,v|puts "#{k}\t#{v}"}
end

GC.start
top_objects

--
String  577159
Array  163891
Hash  93468
Time  50353
ActiveSupport::TimeWithZone  50129
ActiveModel::Errors  23365
TZInfo::TimezonePeriod  23340
ActiveSupport::HashWithIndifferentAccess  23236
RubyVM::InstructionSequence  12484
MIME::Type  1197
MatchData  1183
ActiveModel::AttributeMethods::ClassMethods::AttributeMethodMatcher::AttributeMethodMatch  1128
Class  1101
Regexp  972
Proc  704
Module  655
Gem::Version  619

GCを生き残ったインスタンス数をダンプ。


Vim/Objective-J Pluginに

投稿者 nanki 2010-11-14 20:19:00 GMT

初pull requestが来た!

一人以上ユーザが居ることがわかってよかった。

参考:

github - nanki/vim-objj


高度すぎるコードジェネレータ WLang

投稿者 nanki 2010-10-10 00:14:00 GMT

みなさんはコードジェネレータを使った事があるでしょうか。私はあります。

SQLにHTML, JavaScript, はてはPHPまで、Rubyでコードジェネレータを使う機会は増すばかりです。

さて、Rubyでコードジェネレーションと言えばERbを使うことが多いですが、それで充分なのでしょうか。 他の選択肢はどうでしょうか。

今日は、wlangというコードジェネレータを紹介します。

template engine?

次のサンプルコードを見てください。 とてもシンプルなHTMLテンプレートエンジンのように見えます。

require 'wlang'

puts WLang::instantiate(<<EOS, {:name => "O'Reilly & Animals", :animals => %w(Koala Rhino Giraffe)}, "wlang/xhtml")
<h1>${name}</h1>
<div id="{name}">
  <img alt='{name}' src="http://....."/>
  <ul>
    *{animals as animal}{${animal}}{, }
  </ul>
</div>
EOS

このプログラムの出力結果はこうなります。

<h1>O'Reilly &amp; Animals</h1>
<div id="O'Reilly & Animals">
  <img alt='O\'Reilly & Animals' src="http://....."/>
  Koala, Rhino, Giraffe
</div>

コンテキストに合わせて正しく、'&がエスケープされているのがわかります。 また、記法にはあまり馴染みがありませんが、配列の展開もうまく動いています。

wlang >> template engine

次にwlangの仕組みを見ていきます。 wlangでは、wlangタグを使って、テンプレートを記述します。 wlangタグは次のような形式です。

記号{.....}{.....}{.....}

後ろの{}はblockと呼ばれ、任意の個数持つ事ができます。 記号はたとえば $, *などです。

そう、実は先のテンプレートで出現した、${...}, "{...}, '{...}, *{...}{...}{...} は全て、wlangタグだったのでした。

(ちなみに、”{name}” の末尾の”はwlangタグとは関係なく、ただの文字列です。 これは <div id="{name}'>が文法エラーを出さずに動作する事で確認できます。このセンス嫌いじゃない)

なにやらあやしいかおりがしてきました。

wlang = programming language?

次に、WLang::instantiateの第三引数は”wlang/xhtml”となっていますが、WLangではこれをdialect(方言、なまりの意味)と呼んでおり、当然変えられます。

require 'wlang'

sql = "SELECT * FROM books WHERE name='{name}';"
values = {:name => "O'Reilly"}

puts WLang::instantiate(sql, values, "wlang/sql")
puts WLang::instantiate(sql, values, "wlang/sql/sybase")

# => SELECT * FROM books WHERE name = 'O\'Reilly';
# => SELECT * FROM books WHERE name = 'O''Reilly';

このように、dialect毎に'{...}タグの動作が変わるわけです。

dialectは複数のRuleSetとEncoderSetで定義されています。 RuleSetはタグの機能をオーバライドしたり新たなタグを定義します。 EncoderSetはdialect固有のencoding、つまり文字列の変換方法を定義していて、 ^{..encoding..}{...}というタグで呼び出す事ができます。

以下は大文字に変換するencodingの例です。

# instantiateとか省略します
^{plain-text/upcase}{wlang}
=> WLANG

これが例えばwlang/xhtml dialect であれば wlang/xhtml/main-encoding, wlang/xhtml/single-quoting, wlang/xhtml/double-quoting などが定義され、それぞれ違うエスケープがされるようになっています。

先のwlang/sqlwlang/sql/sybaseの結果の違いも、ここで決まっています。

勘のいい方ならお気づきだと思いますが、${...}, '{...}, "{...}はそれぞれdialectのmain-encoding, single-quoting, double-quotingを呼び出すためのショートカットになっています。

polyglot!

この様な拡張可能かつ再利用可能な変換規則を用意することで、Rubyの文字列を埋め込んだJavaScript、を埋め込んだxhtmlを埋め込んだRubyスクリプト、などといった複雑なコード生成を行う場合でも、 エスケープに悩まされることなく気楽にコード生成を行う事ができるwlangかっこいい!

でも、encodingの実装にバグがあるっぽくて、まだそれは無理っぽい。

参考:

vimwikiすごい

投稿者 nanki 2010-10-05 03:13:00 GMT

紹介しようと思ったのですが、こちらにて詳細にまとめられていたので参照してください。

すごいポイントはいくつかありますが、特にテーブル編集中に自動でセルサイズが再計算されるのが感動です。ただ、pre中でも再レイアウトされるのは残念。

なお、現在のバージョンはいくつか相違点がありました。 * tableのsyntaxがちょっと違う * Vim7.3のconcealに対応している

関係ないけど、作者のMaxim KimさんはVim好きそうな名前ですね。

参考:

Rubyでfinalizerの順番を制御する

投稿者 nanki 2010-09-21 00:55:00 GMT

SWIGでLLVMのRuby bindingを書いていて、やっぱりGC周りではまってしまった。

具体的には、オブジェクトの開放の順番を指定しないといけないのだが、 Rubyではfinalizerの実行順は不定だし、先に消えてほしいオブジェクトからリファレンスを持ったりしてみたのだが、 rootからの参照を一度に消されるとどうしようもない。

そこで試しに以下のようなコードを書いてみたところうまくいった。

module OrderedFinalizer
  @@queue = []

  def self.add(*v)
    @@queue << weaken_reference(v)
  end

  def self.weaken_reference(v)
    v.last.map!(&:object_id)
    v
  end

  def self.step
    @@queue.each do |group|
      unless group.last.any?{|e|ObjectSpace._id2ref(e) rescue nil}
        group.pop
        weaken_reference(group) unless group.empty?
      end
    end
    @@queue.reject!(&:empty?)
    @@queue.empty?
  end

  at_exit do
    GC.start until self.step
  end
end


# obj2, obj3 ☞ obj1 という順番で解放したい
OrderedFinalizer.add [obj1], [obj2, obj3]
OrderedFinalizer.add [obj1], [obj4, obj5]

以下軽く解説。

  1. OrderedFinalizerに解放順序を指定したいオブジェクトへのリファレンスを保持させる。
  2. 一番先に解放してほしいオブジェクトだけはweak reference(つまりobject_id)で持つ。
  3. weak referenceなオブジェクトが解放されていたら、次のオブジェクトの参照をweakにする。

OrderedFinalizerはat_exitで参照されているので、Ruby界では最後まで生き残る。

stepを各GC毎に呼ぶのが理想なのだが、そんなイベントはないよなぁ。 今回の用途には足るんだけど、今のままだと、最後までいくらかのオブジェクトを保持してしまう。

当然、obj1からobj2へのリファレンスがあったりすると無限ループになるし、[true]とか渡してやるとかも困る。

あるいは別の解法求む。