#!ruby -KS

=begin

title: PhiProp
author: YOSHIDA Kazuhiro
mailto: moriq@moriq.com

1. Output 系関数による require 'phi' 時間の短縮

Define 系関数を Output 系関数に置き換え、以下に示す手順で PhiProp.inc を生成し $INCLUDE すると、Define 系関数を使用した場合と比較して require 'phi' での %c:phi.so% の読み込みにかかる時間を短縮できます。

1.1. 手順

--code
D:\apollo\src>dcc32 -B -DMAKE_PROP phi
D:\apollo\bin>ruby_ap -r%c:phi.so% -e0
D:\apollo\src>dcc32 -B phi > out
D:\apollo\src>ruby PhiProp.rb out
D:\apollo\src>dcc32 -B phi
--
+ dcc32 -B -DMAKE_PROP phi
  MAKE_PROP を定義した状態でコンパイルすると Output 系関数が有効になります。
  MAKE_PROP が未定義の場合 Output 系関数は空定義になるのでコンパイラにより削除されます。
  Output 系関数は Ruby クラス・メソッドを定義するための ObjectPascal コードを %c:..\src\PhiProp.inc% に出力します。

+ ruby_ap -r%c:phi.so% -e0
  require 'phi' を評価することで Output 関数を呼び出します。

+ dcc32 -B phi > out
  通常の環境 (MAKE_PROP は未定義) でコンパイルし %c:phi.so% を生成します。
  %c:out% ファイルにはコンパイラからの出力が保存されます。

+ ruby PhiProp.rb out
  変数 klass が未定義であることによるコンパイラ warning を避けるため、%c:out% ファイルをもとに %c:PhiProp.inc% を修正します。

+ dcc32 -B phi
  再び通常の環境でコンパイルします。
  PhiProp.inc での warning がなくなったかどうか確認しましょう。

1.1. Define 系関数と Output 系関数

uProp.pas には Delphi プロパティから Ruby メソッドを自動的に定義できる関数 DefineProp が定義されています。
DefineProp は文字列をもとに RTTI を読むので、多くのプロパティが定義されていたり継承が深かったりすると時間がかかります。
DefineProp は主に Init_* 関数の中で呼び出されます。つまり、Ruby 側でいうと require 時に呼び出されるわけです。require が遅くなる原因はここにあります。

あらかじめ RTTI から Ruby メソッドの定義手続きを作っておけば require にかかる時間を短縮できます（そのかわり %c:phi.so% のサイズは大きくなります）。
OutputProp は DefineProp と基本的に同じロジックですが、Ruby API を呼び出す代わりにその ObjectPascal コードを出力します。
同様に DefineClassName, DefineAttrMethod, DefineAttrMethod_retval, DefineConstSetType に対応する OutputClassName, OutputAttrMethod, OutputAttrMethod_retval, OutputConstSetType を用意しました。

今のところ Output 系関数は %c:phi.so% でのみ用いています。Phi 拡張ライブラリでは依然として Define 系関数を用いています。Phi 拡張ライブラリで Output 系関数を使うことも考えられますが、コンパイルの手順が煩雑になるので今のところ対応する予定はありません。

=end

# compile warning の出た行を得る
rows = []
while gets("\n\r")
  if ~ /^PhiProp.inc\((\d+)\) ./
    rows.push $1.to_i
  end
end

# CLX にはないもの
ONLY_VCL_CLASSES = %w/
Frame
StaticText
RichEdit
UpDown
PageScroller
Animate
/
# IFDEF VCL のスイッチ
in_ifdef_vcl = nil

fname = 'PhiProp.inc'
fntmp = fname + '~'
File.rename fname, fntmp
open(fntmp, 'r'){|i|
open(fname, 'w'){|o|
  while i.gets
    if ~ /klass := c(\w+);/
      cname = $1
      if ONLY_VCL_CLASSES.include? cname
        unless in_ifdef_vcl
          o.print "{$IFDEF VCL}\n"
          in_ifdef_vcl = true
        end
      else
        if in_ifdef_vcl
          o.print "{$ENDIF}\n"
          in_ifdef_vcl = nil
        end
      end
    end
    if rows.include? $.
      o.print '//'
    end
    o.print
  end
  if in_ifdef_vcl
    o.print "{$ENDIF}\n"
    in_ifdef_vcl = nil
  end
}}
