diff options
author | Yukihiro Matsumoto <matz@ruby-lang.org> | 1996-12-24 15:20:58 +0900 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2019-08-17 22:09:32 +0900 |
commit | 554b989ba1623b9f6a0b76f00824c83a23fbcbc1 (patch) | |
tree | 71f06227fe259bebaa5ca4bf05cc398184bced68 /eval.c | |
parent | fca49a8a69a0f6bb4feae74c6cd0e93d7fac8b36 (diff) |
version 0.99.4-961224v0_99_4_961224
https://cache.ruby-lang.org/pub/ruby/1.0/ruby-0.99.4-961224.tar.gz
Tue Dec 24 15:20:58 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.4-961224
* configure.in: charがunsignedかどうかもチェック
* regex.c (SIGN_EXTEND_CHAR): __CHAR_UNSIGNED__にも対応
* pack.c (pack_unpack): 明示的にsigned charを指定.
Mon Dec 23 14:41:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ruby.c (load_file): 標準入力からのスクリプトで一時ファイルを使わ
ないように
* object.c (f_integer): `0x', `0'などでbaseを解釈するように.
Fri Dec 20 01:44:39 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* Makefile.in (flock.o): flockに対応
Thu Dec 19 20:13:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.4-961219
Wed Dec 18 00:06:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* glob.c (glob_filename): strrchrがマクロの場合に対応
* configure.in: <sys/select.h>をチェック
* ext/kconv/kconv.c: 1.62ベースに
* ext/kconv/kconv.c: Kconvモジュール
* string.c (str_substr): lenが元の文字列より長い時に対応
* parse.y (iterator): 「$bar do .. end」などは許さないように
* parse.y (iterator): FID(foo!,foo?)をdo形式のイテレータにできる.
* missing/flock.c (flock): lockf()を使って代替
* file.c (file_flock): flockを実装
Tue Dec 17 12:13:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.4-961217
Fri Dec 13 02:05:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* configure.in: RUBYLIBのカレントを後回し(@mix/awk offline)
* dln.c: AIXに対応した?(@mix/awk offline)
* eval.c (thread_schedule): critical sectionでも明示的なコンテキス
トスイッチは起きないとまずい
* re.c (reg_search): matchに失敗した時に$~をnilに.
* re.c (reg_search): 毎回matchを生成するように
Thu Dec 12 17:03:30 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* numeric.c (flo_to_s): 2.0.to_s -> 2.0に
* eval.c (thread_save_context): $_, $~をthread毎に保存
* eval.c (thread_kill): main threadではexit(0)
* string.c (str_split_method): 間違った結果を返していた
Thu Dec 12 15:32:48 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
* dir.c: CYGWIN32対応
* ext/socket/socket.c: CYGWIN32対応
* io.c: CYGWIN32対応
Thu Dec 12 14:43:51 1996 Jun Kuroda <j_kuro@pluto.ai.kutech.ac.jp>
* lib/tk.rb: wish4.2も探索候補に含める
* config.guess: JCC対応
Thu Dec 12 00:41:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.4-961212
* parse.y (parse_string): """..."""はやはり無くすことにした
* parse.y (parse_regx): %r|...|でterminatorを \ でエスケープできる
ように
* signal.c (posix_signal): sigactionを使うsignal
* configure.in: posix signal/bsd signalの検出
Wed Dec 11 17:47:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_schedule): critical sectionではコンテキストスイッ
チが起きないように
* lib/thread.rb: SharedMutexクラス
* lib/jcode.rb: String#scanを使うように
Tue Dec 10 12:21:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961210
* string.c (str_split_method): 正規表現に()を含む時にバグ
* lib/jcode.rb: ちょっとましになった
* string.c (tr_setup_table): 置換文字が短すぎる(2文字)のときのバグ
Mon Dec 9 11:38:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_scan): 文字列のマッチを行う.イテレータとしても動
作する
* regex.c (re_copy_registers): allocatedが初期化されていなかった
* re.c (match_to_s): $~の文字列化
* re.c (match_to_a): $~を配列化できるように
* re.c (match_getter): レジスタが初期化されていなかった
Thu Dec 5 11:06:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_split_method): マッチしなかった括弧は空文字列を
pushするべきではない
* string.c (str_succ): アルファベットを含まない文字に対応
Wed Dec 4 10:48:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961204
* io.c (io_binmode): DJGPPでのbinmode対応
* sprintf.c (f_sprintf): intの範囲の数値は直接sprintfで変換する
* sprintf.c (f_sprintf): "%02s"に頼らない
* re.c (reg_search): indexでSEGV
Tue Dec 3 10:09:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961203
* ext/extmk.rb.in (install): INSTALL_DATAからINSTALLに変更
* dln.c: hpux対応
* string.c (str_aset_method): 負の値を含む範囲でも例外を起こさない
* array.c (ary_replace): 負の値を含む範囲でも例外を起こさない
* array.c (beg_len): beg==endの時,長さ0に
Mon Dec 2 14:07:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* configure.in: HP shl対応
* string.c (str_upto): beg > endの時無限ループに落ちるのを止めた
* range.c (range_each): String#uptoが再定義された場合に対応
* string.c (str_split_method): "ABC".split(/(B)/)が誤動作
Sat Nov 30 01:43:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_eval): undefでSEGV
Fri Nov 29 12:17:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-parse-region): %Q#..#などに対応.しか
し,区切り文字が演算子で行末にある場合には対応できなかった.
* re.c (reg_raise): 例外でもスラッシュをエスケープ
* re.c (reg_inspect): スラッシュをエスケープ
* parse.y (parse_string): `%[QqXxRr](.)..\1'なる文字列形式(テスト
採用)
* parse.y (parse_qstring): '''...'''の形式
* ext/dbm/dbm.c (Init_dbm): 述語key?,value?の追加
* ext/dbm/dbm.c (Init_dbm): includes->include?
* hash.c (Init_Hash): 述語key?,value?,include?の追加
* eval.c (rb_eval): else節が実行されない(うーん)
* string.c (str_sub_iter_s): イテレータブロック内でマッチが行われ
ると位置がずれる(時に無限ループに落ちる)
* string.c (str_resize): lenが0の時sizeの調整が行われなかった
Thu Nov 28 00:59:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961128
* parse.y (parse_string): 3-quote styleの文字列(例:"""abc"d"e""")
* configure.in (EXTSTATIC): extを静的にリンクする時にはrubyはdllを
使うように
* io.c (Init_IO): getsの引数が間違っていた
* string.c (str_each_line): RSを明示的に指定できるように
Wed Nov 27 12:37:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961127
* eval.c (rb_eval): iver defined? でselfを指定するのを忘れた
* io.c: gets等でRSを明示的に指定できるように
* ext/extmk.rb.in (install): static linkに失敗
Tue Nov 26 10:33:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961126
* string.c (str_sub_s): 置換後の文字列長さが間違っていた
Mon Nov 25 09:11:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* numeric.c (fix_rshift): 32以上の右シフトで0を返すように(Cの
rshiftは(x>>(y%32))を返していた).
* string.c (str_gsub): 置換が行われない場合があった
* string.c (str_resize): 本当に必要な時だけrealloc
Thu Nov 21 04:13:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* configure.in (EXTSTATIC): --with-static-linked-extで全てのモジュー
ルを静的リンクするように
* pack.c (pack_unpack): 行末の改行がない時にもチェックサムをスキッ
プするように
Wed Nov 20 96 21:42:51 1996 Yasuo OHBA <jammy@shljapan.co.jp>
* configure.in: freebsd対応
Wed Nov 20 10:24:24 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/extmk.rb.in (install): 通常リンク用のLDFLAGSとダイナミックリ
ンク用のDLDFALGSを分離
* ext/extmk.rb.in (install): コンパイルの成功したものを静的リンク
のリストに追加する
* eval.c (f_missing): オブジェクトの文字列表現が長すぎる時バッファ
を書き潰していた
* process.c (proc_exec_v): forkした後例外を発生させてはいけない
Tue Nov 19 13:28:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961119
* eval.c (mod_method_defined): Module#method_defined? の追加
* parse.y (call_args): 引数が唯一のコマンドコールである時のバグ(戻
り値が展開されてしまう)
Mon Nov 18 13:28:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_sub): 失敗した時にnilを返していた
* string.c (str_split_method): 検索開始位置が移動してなかった
* ext/socket/socket.c (sock_s_getservbyaname): まだ間違っていた
* version 0.99.3-961118
* string.c (str_sub_s): 元の文字列を置換するのを止めた
* pack.c (encodes): 領域外をアクセスしていた
Fri Nov 15 17:10:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* bignum.c (big_divmod): Bignumが引数の場合の対応忘れ
* sample/ruby-mode.el (ruby-expr-beg): word?形式への対応が不完全
Wed Nov 13 15:42:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_tr_s_bang): tr_sでtrが行われていなかった
* eval.c (rb_eval): autoloadクラスのチェック
* string.c (f_sub): subがsub!と同じ動作になっていた
* eval.c (thread_sleep): stopとsleepの分離
Mon Nov 11 13:53:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961111
* numeric.c (fix_step): to, stepが整数以外の場合に対応
* eval.c (rb_call): dynamic varがdynamic scopingになっていた(これ
はまずい)
* string.c (str_chop_bang): 長さ0の文字列のchopで,領域外のアクセ
スが発生していた.
* parse.y (yyerror): 割り当てた領域外をアクセスしていた
Fri Nov 8 11:54:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_yield): scopeをheapにコピー
Thu Nov 7 09:56:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* numeric.c (num_coerce): とりあえず両辺をFloatに変換することに
Wed Nov 6 10:45:13 1996 Yasuo OHBA <jammy@shljapan.co.jp>
* lib/parsearg.rb: 第2引数を変更.
Tue Nov 5 14:21:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961105
Sat Nov 2 01:11:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* bignum.c (big_pow): typo (dy -> dx)
* bignum.c (big_divmod): 知らない型はfloatに変換してみる
* numeric.c (fix_lshift): 境界条件のバグ(負になっていた)
* bignum.c (big_pow): 無駄なfloatへの変換をなくした
* math.c (math_atan2): typo(x -> y)
Fri Nov 1 15:30:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/socket/socket.c (sock_gethostname): gethostnameがない時には
unameを使ってホスト名を得る
* ext/etc/etc.c (etc_getlogin): getloginがNULLを返しても環境変数を
調べるように
* object.c (krn_clone): オブジェクトのフラグもコピー
* hash.c (rb_cmp): ハッシュの比較を`=='でなく`eql?'に変更
* math.c (Need_Float): Float()を使って変換する
* compar.c (cmp_gt): 以前の右辺を返す仕様の名残が残っていた
Thu Oct 31 12:55:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961031
* numeric.c (Init_Numeric): typo
* eval.c (error_print): 長すぎるtrace backを途中省略する
* regex.c (re_compile_pattern): 全角のrangeに対応
Wed Oct 30 03:03:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.3-961030
* io.c (f_ungetc): 関数を追加
* eval.c (dyna_var_asgn): return値忘れ
Tue Oct 29 10:05:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (f_split): 関数splitを追加
* eval.c (rb_call): ネストした外側のクラス/モジュールの定数を参照
できるように
Mon Oct 28 09:51:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_sub): offsetが文字の末尾にある時のチェック
* regex.c (re_match): 割り当てるレジスタの数が1多かった
* io.c (io_gets): $/ = ""の動作をperlに合わせる(awkとはちょっと違
うらしい)
* io.c (io_gets): $/ = nilの時少し高速化
* string.c (str_split_method): 括弧がnullにマッチした時にも無視し
ないように
* string.c (str_split_method): 括弧にマッチした分はlimitの数に含め
ないように.
* numeric.c (num_coerce_bin): coerceの定義を変更,2要素の配列
[x,y]を返すように
* sample/ruby-mode.el (ruby-calculate-indent): "do |aa|"の対応を改
善した.
Sat Oct 26 01:43:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/marshal/marshal.c (w_object): ビルトインクラスのサブクラスを
正しく復旧できるように
* ext/marshal/marshal.c (w_object): ユーザ定義dumpの優先
* numeric.c (flo_coerce): Float()を使って定義
* numeric.c (Init_Numeric): Numericのnewのundefはまずい
* ext/marshal/marshal.c (w_symbol): シンボルの内容(文字列)は一度し
かファイルに書き出さない.
* sample/ruby-mode.el (ruby-parse-region): if/while修飾子に対応し
なくなっていた
* bignum.c (Init_Bignum): Bignum.newを除く
* eval.c (rb_eval): 引数評価後にファイル名と行番号を再設定
* numeric.c (flo_div): typo
* sample/ruby-mode.el (ruby-parse-region): def /, def `に対応
Fri Oct 25 09:26:29 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-calculate-indent): "do |aa|"に対応
* array.c (ary_aset): indexがfixnumの場合ちょっと高速化
* eval.c (thread_fd_writable): 書き込み前のselectチェック
* array.c (ary_assoc): 無限ループに落ちた
* eval.c (thread_wait_for): selectがエラー終了した時,linux以外で
の動作が正しくなかった.
Thu Oct 24 08:26:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (backtrace): `$@'を文字列から配列に変更した.
* eval.c (eval): eval中の例外発生位置を保存する
* bignum.c (bigsub): オペランドの大小比較の失敗
* re.c (reg_search): 直接参照がない時にも`$~'がセットされるように
Wed Oct 23 10:40:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-961023
* ext/marshal/marshal.c (r_bytes): mallocをやめ,allocaを使う
* sample/ruby-mode.el (ruby-calculate-indent): 括弧の対応を変更.
()内ではインデントをレベルを合わせるように
Tue Oct 22 12:59:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* hash.c (hash_s_new): sizeを指定できるように
* ext/marshal/marshal.c (w_object): dumpする深さ制限を指定できるよ
うに
* array.c (ary_s_new): sizeを指定した時の初期化忘れ
* object.c (f_float): big2dblの宣言忘れ.
* bignum.c (bigsub): 大きさの近いBignum同士の演算で結果が負になる
場合に間違いがあった.
* array.c (ary_aset): 置換先と置換元が同じ長さの時内容を
shift(memmove)しないように.
* ext/marshal/marshal.c (marshal_dump): ファイルフォーマットにバー
ジョンを埋め込むように
* ext/marshal/marshal.c (tmpnam): linux-aout-dln用に定義
Mon Oct 21 08:40:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/socket/socket.c (sock_s_gethostbyname): hostent構造体の情報
を返す
(sock_s_gethostbyaddr): IPアドレスからhostent構造体を得る
(sock_s_getservbyaname): getservbyname(3)
Fri Oct 18 10:37:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-indent-to): 移動先カラムが負になるバグ
* eval.c (compile): evalで元ソースの行番号でエラーを表示する
Thu Oct 17 09:52:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (eval): evalで文法エラーがあった時にSEGV
* lib/safe.rb: Restricted.evalの中だけ制限を加える.
* eval.c (error_print): バックトレースの出力.callerで例外発生位置
を調整した時に問題が出る(そんなことをしなければ良いのだが…)
* eval.c (make_backtrace): バックトレースの生成
Wed Oct 16 12:56:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ruby-man-0.99.2-jp/index.html: 日本語版ドキュメントの完成(長かった…)
* re.c (reg_regcomp): $=がnilの時の処理
* string.c (f_chop): $_に対するchop
Tue Oct 15 11:04:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-961015
Mon Oct 14 18:22:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_schedule): BOW対応.selectが-1を返した時にバグ(実
はdo .. whileがcontinueで先頭にジャンプすると思い込んでいた.条
件の直前だったのね ^^);;;;;
* sample/ruby-mode.el (ruby-mode-syntax-table): ?のsyntaxが"/"では
まずいらしい
* hash.c (rb_hash): name conflict
Fri Oct 11 00:23:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-961011
* ext/marshal/marshal.c (w_object): 結局動いていなかった循環オブジェ
クト対応を外した.
* hash.c (rb_hash): Fixnumと文字列の高速化
* ext/marshal/marshal.c (w_object): 無駄なデータの削除(フォーマッ
トの非互換性)
* io.c (io_readline): 戻り値の不備
* ext/marshal/marshal.c (marshal_dumps): MSDOS対応
* ruby.c (load_file): MSDOS対応
Wed Oct 9 17:46:27 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/extmk.rb.in (install): 無駄なコピーを避ける
* string.c (str_sub_method): マッチがなかった時のString#subの値が
違っていた.
* eval.c (obj_extend): extendした時にobject_extendedを呼ぶように
Tue Oct 8 00:55:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_alloc): 割当の平均化
* eval.c (thread_schedule): joinのバグを修正
* eval.c (thread_wait_for): selectへの割込みなどに対応
* eval.c (thread_select): linuxのselectの挙動に対応(timeoutが変化
する)
Mon Oct 7 09:47:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-961007
* eval.c (PUSH_BLOCK): the_classの保存を忘れていた.
* ext/dbm/dbm.c (fdbm_store): sizeの保存する場所が間違っていた
* ext/socket/socket.c (s_accept): thread対応していなかった
Sat Oct 5 01:32:27 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* io.c (io_readchar): EOFで例外を発生させる
Fri Oct 4 11:59:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/marshal/marshal.c (w_object): HashとObjectの復旧に必要なハッ
シュテーブルが渡されていなかった.
* variable.c (rb_path2class): ユーザ定義クラスの復旧に失敗していた
* variable.c (rb_path2class): クラスが存在しない時のエラーをFatal
からNameErrorへ.
* range.c (range_s_new): first,lastが両方Numericの時エラーになって
いた.
* range.c: start->first, end->last
Wed Oct 2 02:02:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* file.c: DJGPPでchmod,chownを使えるように(ってDOSにchownがあるのか?)
* class.c (rb_singleton_class): ビルトインクラスもextendしたり特異
メソッドを追加したりできるように
* variable.c (rb_set_class_path): ユーザ定義のトップレベルクラスに
pathを設定しない
* eval.c (eval): 例外がRuntimeErrorに化けていた
* eval.c (eval): eval中の例外の表現の改善
* eval.c (eval): eval_with_bindingとの一本化
* eval.c (rb_eval): クラス/モジュール定義の中から定義中のクラス/モ
ジュールが参照できるように
Tue Oct 1 01:40:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-961001
* parse.y: cur_crefが2度宣言されていた
* signal.c (trap): SIGSEGV,SIGBUSのない機種に対応
* io.c (Init_IO): 引数タイプの指定間違い
Mon Sep 30 15:28:00 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-960930
* config.guess,config.sub: $host_osが正しく設定されない
* eval.c (rb_eval): yieldで正しくないselfが設定されていた
* eval.c (ruby_run): toplevelの例外処理のバグ
Mon Sep 30 09:13:26 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
* djgpp対応
Sat Sep 28 02:45:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-960928
* sample/ruby-mode.el (ruby-beginning-of-block): ブロックの先頭に
移動(正しくインデントしていないと動作しない)
(ruby-end-of-block): 同上
* eval.c (class_s_new): Class#newがイテレータとして呼ばれた時は
initializeもイテレータとして呼ばれるように
* signal.c (sigsegv): SEGVでbacktraceを表示するように
Fri Sep 27 09:51:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-960927
* eval.c (error_print): 引数のないraiseでメッセージが正しく表示さ
れるように.
* eval.c (rb_longjmp): mesgがnilの時RuntimeErrorを生成する.
* eval.c (f_raise): 引数がない時に対応
* eval.c (thread_mark): stack上にないデータのアドレス変換を行って
いた.
* eval.c (Init_Thread): 割込みの間隔が1秒と長すぎた.
Thu Sep 26 16:02:45 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_schedule): 一度ペンディングになるとフラグがクリア
されていなかった.
* process.c (rb_proc_exec): system/execの引数が空文字列であった場
合,例外を発生すべきだった.
* config.sub/config.guess: 新しいものに置き換え
Thu Sep 26 15:41:35 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
* io.c (next_argv): -i.bakをBOWとDOSに対応.
Thu Sep 26 01:31:43 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* io.c (io_sysread): EOFで例外
* io.c (f_readline): EOFで例外を発生するように.getsは互換性のため
nilを返すままにする
* eval.c (proc_call): lambdaからのreturnでIN_BLOCKフラグが立ったま
まだった
* eval.c (PUSH_BLOCK2): threadに対応するためBlockを一度stackにコピー
Wed Sep 25 11:54:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (method_call): Const::method()形式を使えるようにしてみた.
引数括弧は省略できない.
* sample/test.rb: Process.killの存在を確かめてからテストを行う
* eval.c (eval_with_binding): 第2引数としてbinding(またはlambda)を
与えるとその環境でevalを実行するようにした
* eval.c (f_binding): 現在のbindingを返す関数
* eval.c: block構造体にthe_classを保存するメンバを追加
* process.c (Init_process): kill,wait,waitpidをProcessに移動
Tue Sep 24 02:44:43 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el: いろいろ問題が多いので以前の高速化は破棄.
別のアプローチを使った.
* lib/tk.rb (Tk.pack): 複数のウィンドウを受け付けるpack
Sat Sep 21 11:08:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (exprs): 空文も受け付けるように文法を変更.今までは改行
の連続だけが許されていた.
Fri Sep 20 11:39:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* Failの大半を名前つき例外に変更.
* re.c (Init_Regexp): 名前つき例外を導入.
* eval.c (f_missing): Objectはinspectしない.
* object.c (inspect_i): Object#inspectでloopに対応.
* regex.c (re_search): /^$/が""にマッチしなかった.
Thu Sep 19 19:25:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* regex.c (re_search): /^$/が非空行にマッチしていた.
Tue Sep 17 10:28:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-960917
Mon Sep 16 10:47:56 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-calculate-indent): 演算子継続の場合の
文字列の判定のバグ
* sample/ruby-mode.el (ruby-calculate-indent): elseなどの次の行の
インデント計算を正しく.
Sat Sep 14 08:37:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.2-960914
Fri Sep 13 08:06:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/socket/socket.c (tcpaddr): port番号にntohsをつけ忘れ
* dln.c (link_undef): テーブルの種類が間違っていた.
* bignum.c (bigadd): 引き算が発生する時に計算違いが起きていた.
* parse.y (iter_do_block): do..endでもdynamic variableを.
* bignum.c (big_pow): より正確な計算を(整数同士ではfloatに変換しな
い).
Thu Sep 12 13:11:55 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* variable.c (rb_set_class_path): Stringクラスが初期化される前に
Stringを作っていた.組込みクラスにはpathはいらない
* parse.y (yylex): 0.1が0になっていた
* parse.y (yylex): 行番号の不整合
* gc.c (oblist_live_obj): 今「生きている」全部のオブジェクトを返す
イテレータ.そのクラス(またはサブクラス)の全部のインスタンスを返
すeach_object_ofも定義した.
* class.c (rb_define_class_id): 無駄なクラスを割り当てていた.結果
として未初期化のクラスオブジェクトが存在していた.
Wed Sep 11 00:56:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (yylex): octalの定数の検出をより正確に(090はエラーとか).
* bignum.c (big_minus): yがxより大きい場合にエラー.
* parse.y (yylex): エラー行番号の表示をより正確に
* sample/ruby-mode.el (ruby-expr-beg): 変数名が1文字の時誤動作して
いた.
* sample/ruby-mode.el (ruby-calculate-indent): ?/でループに落ちい
たバグを修正.
* enum.c (enum_min,enum_max): sortのようにイテレータとしても動作す
るように.
* enum.c (enum_find_all): typo
Tue Sep 10 12:07:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* node.h (nd_line): NODEのlineをflagsに押し込めてオブジェクトサイ
ズを小さくした.制限:32bit intのマシンの場合,ファイルの行数が
32767を越えると正常に表示されない.
* st.c: hashとcompareの関数メンバを構造体にパック,クラス的な使い
方を行う.1 tableあたり4 byteの節約.
Mon Sep 9 16:35:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* file.c (file_truncate): 提供されない時には特別な例外を発生するよ
うに.
* eval.c (Init_Proc): 不適切な位置のlocal-jumpを例外に.
Sat Sep 7 17:06:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (proc_call): まだスコープがスタック上にある時には局所脱出
を有効にする.これで,procを生成してcallすることは,スコープを脱
出しない限り,yieldと同じ意味を持つことになる.
Fri Sep 6 13:30:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-indent-to): インデントが変わらない時に
はバッファを変更しない.
(ruby-calculate-indent): まず文字列の内部か判断してから,前の行
からパーズを行う.defunが大きくなった時の高速化.
(ruby-in-string-p): 文字列の内部かどうかを判断する関数(以前の
parseから分離)
(ruby-parse-region): 文字列に対する処理をはずす.
(ruby-beginning-of-block): ブロックの先頭に
(ruby-end-of-block): ブロックの末尾に(遅い…)
Thu Sep 5 14:23:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* file.c (file_s_split): [dirname,basename]にsplitする.
* eval.c (rb_eval): evalの中でも定数の値が正しくなるように.これで
定数に関しては静的なスコープが保証されるようになった.
* st.c (rehash): ハッシュ拡大の系数を2から1.79に.割算がより良い値
を返すように.
Thu Sep 5 00:32:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (class_superclass) クラスのスーパークラスを返すメソッド.
Wed Sep 4 16:54:56 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* random.c (f_rand): Bignumやlongの範囲を越えるFloatに対する乱数も
発生できるように.
* struct.c (struct_alloc): Fatalではなく例外を発生させるように(通
常の使用で発生しうる).
* struct.c (struct_s_members): Structの特異メソッドではなく,生成
されたStructクラスの特異メソッドにした.
* st.c (st_init_table): ruby専用にパラメタを固定にした(サイ
ズが減った)
Mon Sep 2 11:37:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* array.c (ary_shift): capaがあまりにも大きい時には領域をREALLOC
(ary_pop): 同上
* string.c (str_inspect): multibyte character 対応にミス.
(str_inspect): unsigned charにしないと符号展開されてしまう
* parse.y (primary): `::'をprimaryに移動 Foo::Bar.Bazがエラーにな
らないように.
* parse.y (primary): オペレータ形式の特異メソッドが定義できない
* random.c (f_rand): maxが0の時に対応
* io.c (io_printf): 関数を定義していたがインタプリタに登録していな
かった.
* file.c (file_s_basename): 第2引数が無い時にエラー.
Thu Aug 29 10:49:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (expr): イテレータの新形式に「method do .. end」形式を採
用した.もちろん昔の形式も有効.
* sample/ruby-mode.el (ruby-calculate-indent): endの数の方が多い場
合にもエラーを起こさないように.
Wed Aug 28 09:41:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* numeric.c (upto,downto,step,times): 対象がfixnumの範囲を越えても
動作するように.
Mon Aug 26 10:04:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* missing/setenv.c (envix): typo(missing `== 0' for memcmp)
* dir.c (dir_foreach): foreach(dir open -> read loop -> closeまで)
* io.c (io_foreach): foreach(file open -> read loop -> closeまで)
* Fatalのうち捕捉可能ないくつかを例外に.
Sat Aug 24 23:56:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* bignum.c (bigdivmod): FIX2INT -> INT2FIX 大間違い
Fri Aug 23 18:13:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* regex.c (re_free_registers): allocateしていない時には当然 free
してはいけない.
Thu Aug 22 01:20:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_create): 外側から強制終了させられたthreadは
cleanupする必要が無い.
Wed Aug 21 09:57:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_create): threadを終了させた大域脱出の情報を
main_threadに渡すように.
* parse.y (call_args): 最終引数に括弧を省略したメソッド呼出しを置
けるように(例: print foo bar, baz == print(foo(bar,baz)))
Tue Aug 20 13:37:16 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (masign): 多重代入とrest引数の動作を合わせて空の配列を代
入するように.
* parse.y (arg): defined?の強度をもうちょっと強く
* eval.c (error_print): -wで例外名も表示するように
* eval.c (rb_eval): 新構文に対応
(handle_rescue): 捕捉する例外を kind_of? で同定
* parse.y (primary): rescueの構文を変更(同定引数の追加,複数rescue)
* Fail()のかなりを適当な例外を使うように
* eval.c (thread_interrupt): Interrupt(今はnon-local jump)は
main-threadに送られるように.
* eval.c (rb_longjmp): $! の内容を文字列から例外クラスに変更
(rb_raise): rb_fail から名称変更
(rb_interrupt): 例外化
(rb_exit): 例外化
* error.c (Init_Exception): 例外クラスの新設(文字列のサブクラス)
Mon Aug 19 19:40:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* signal.c (trap): 古いハンドラを返すように.
Wed Aug 14 00:07:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_trap_eval): ハンドラのためにthreadをforkすることを止
めた.
* eval.c (thread_mark): thread毎の $!, $@ をマークし忘れ
* ext/dbm/dbm.c (fdbm_delete): イテレータとして呼ばれた場合,要素
が無ければブロックを評価する.
* hash.c (hash_delete): イテレータとして呼ばれた場合,要素が無けれ
ばブロックを評価する.
* array.c (ary_delete): イテレータとして呼ばれた場合,要素が無けれ
ばブロックを評価する.
* eval.c (rb_interrupt): SIGINTのデフォルトをexitから特別な大域脱
出に.やはり割り込まれた位置の表示が無いのは寂しいので.
Tue Aug 13 01:34:00 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_exit): sub-thread内でのexitもstatusを保存するように
(thread_create): 自thread内のexitに対応
* signal.c (sighandle): SIGINTのデフォルトハンドラはexitするように
(以前は例外を発生していた).
* 例外の一部をFatalに.
* string.c (str_aset): 文字列の置換の対象が部分文字列でなかった時,
例外を発生させないように
* eval.c (proc_call): Procの中からbreak/nextは通し,他のものは通さ
ないように
Mon Aug 12 14:15:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* object.c (krn_type): 文字列を返す
* eval.c (thread_create): sub-thread内でのexitに対応
* numeric.c (fix_type): 文字列を返す
* io.c (f_p): デバッグ用データ表示メソッド
* eval.c (f_missing): nil/TRUE/FALSEを特別扱い
* string.c (str_inspect): 長い文字列を短縮表示.inspectの働きを
human readable stringの生成に統一(re-generatable string は正式に
無くなった).
Sat Aug 10 16:54:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* object.c (Init_Object): kernel/nil/false/trueのクラス名を変更(小
文字に),rubyスクリプトからアクセスできないように.
* eval.c (rb_eval): CONSTANTのアクセス先を単純化.crefを使わない.
* eval.c (f_eval): 特異メソッド内でも定数の値が正しくなるように
Fri Aug 9 12:23:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* array.c (ary_concat): append -> concat Stringに合わせた
* parse.y (yylex): `$;'が使えなかった.
* array.c (ary_push_method): 複数引数を受け付けるように.
(ary_unshift): 複数引数を受け付けるように.
* io.c (io_popen): IO.popenでcommand pipeが開けるように.
* object.c (Init_Object): KernelとNilをruby scriptからアクセスでき
ないように.
Thu Aug 8 01:21:47 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* object.c (f_integer): 整数への変換関数
(f_float): 実数への変換関数
(f_string): 文字列への変換関数
(f_array): 配列への変換関数
* bignum.c (big_to_i): FIXNUMの範囲でない時はBignumのまま返すよう
に変更.
Wed Aug 7 09:28:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99.1-960807
* parse.y (mlhs): 「*foo = 1,2,3」タイプの多重代入も可能に.
* object.c (Init_Object): クラスTrue/Falseをruby scriptからアクセ
スできないように.
* object.c (nil_inspect): inspect表現は"nil"に
* io.c (io_print): nilのprintをnilに.
* object.c (nil_to_s): nilの文字列表現を""に.
Tue Aug 6 01:12:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* dir.c (dir_s_open): file descripterが足りない時にはgcしてからも
う一度openしてみる.
* io.c (rb_fopen): すべてのfopen()についてfile descripterが足りな
い時にはgcしてからもう一度openしてみる.
* ext/socket/socket.c (Init_socket): 定数の追加.
* sample/ruby-mode.el (ruby-indent-to): インデント後のカーソル位置
の調整を正しく.
* gc.c (gc): 割込みチェックを行わない(Cコードの中で安心して
malloc()が使えなくなるので).
* st.c (call_hash_func): signalとthreadによる割込みに対応.
* sig.h (DEFER_INTS): 割込み禁止区間の指定
* eval.c (f_require): threadによるrequireの競合に対応(最初の
requireが終了するまで他のthreadは待つ).
* bignum.c (str2inum): 0x80000000の値が負になっていた
* sprintf.c (f_sprintf): 文字列末尾,行末の単独の`%'に対応
* bignum.c (big_cmp): 比較の結果が逆になる時があった.
Mon Aug 5 10:58:13 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* process.c (proc_exec_v): 例外のメッセージを分かりやすく.
* ext/dbm/dbm.c (fdbm_store): nilを格納すると要素の削除になる
* ext/dbm/dbm.c: サイズをキャッシュ.
Sat Aug 3 01:52:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_fail): `fail'が引数無しで呼ばれた時だけ以前の`$@'を保
存するように.
* eval.c (f_fail): frameの調整
Fri Aug 2 11:26:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/socket/socket.c (bsock_setopt): valとしてTRUE/FALSE/Fixnumも
受け付けるように.
* ext/socket/socket.c (Init_socket): SO_REUSEADDR等の定数の追加
* ext/md5/md5init.c: md5モジュール(初の複数ファイルからなるモジュー
ルでもある)
* ruby.h (Make_Data_Struct): Data: objectのinstance変数に格納 ->
Data型のObjectに(Dir,Time,Proc,Thread,DBM)
Thu Aug 1 11:38:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/dbm/dbm.c (fdbm_store): valueが文字で無い時に対応
Wed Jul 31 10:53:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ext/socket/socket.c (open_inet): htonsが必要であった
(tcpaddr): ntohlで変換した
* process.c (rb_proc_exec): execvp -> execv
Tue Jul 30 17:48:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c: `$?'をthread localに
* Makefile.in (install): install時にstripを行う
* configure.in: install時のstripの検出
* configure.in: NEXTSTEP対応
* version 0.99.1-960730
Tue Jul 30 16:40:35 1996 SHIROYAMA Takayuki <psi@fortune.nest.or.jp>
* dln.c (dln_load): NeXT dln(mach-o)対応.configureは未対応
Tue Jul 30 09:46:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* process.c (f_system): 複数引数もとれるように
* process.c (f_exec): 複数引数もとれるように
* array.c (ary_append): 配列(またはEnum)の要素を破壊的に追加
* array.c (ary_plus): Enumはその要素を追加
* file.c (file_s_open): File.openを追加
* struct.c (struct_new): FIX2INTを忘れていた
* file.c (Init_File): exists? -> exist?
* object.c (obj_is_kind_of): is_kind_of? -> kind_of?, is_a?
* object.c (obj_is_instance_of): is_instance_of? -> instance_of?
Mon Jul 29 16:40:02 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (parse_regx): 式展開を行った場合,casefoldの設定ができて
いなかった.
* object.c (true_type): TRUE/FALSEにtypeを実装.
* parse.y (read_escape): 3文字以内のoctalに対応(\0とか)
Fri Jul 26 00:31:45 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* array.c (ary_reverse_bang): in-placeで配列を反転させる
(ary_sort_bang): in-placeでsortする
(ary_sort): sortした配列を返すように
(ary_delete_at): 指定した位置の要素を削除する
* eval.c (rb_call): stack深さチェックを毎回は行わないように
* error.c (Warning): 実行中のwarningが表示されていなかった
* eval.c (compile): 例外発生を分離.
* eval.c (f_eval): 変数rb_in_evalを正しく管理するように
* ext/dbm/dbm.c (fdbm_store): 格納するkeyを文字列に変換
* eval.c (rb_call): 無限再帰のチェックを大域脱出を行うC methodにも
対応させた.threadのstack深さチェックルーチンを流用.
* parse.y (yylex): 第1引数のunary -/+の判定が間違っていた.
* parse.y (yylex): unary +で数字を余計に読んでいた(ex. +5 -> 55)
Thu Jul 25 12:15:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (yylex): 曖昧でない引数に対して警告を出していた.
* eval.c (iterator_p): 引数で呼んでも正しい結果を返すように.
* parse.y: break/next/redo/retryのメソッド化.
* sample/ruby-mode.el (ruby-calculate-indent): nestのチェックミス
* sample/ruby-mode.el (ruby-parse-region): 予約語のチェックを強化
* parse.y (primary): unless/untilの復活
Tue Jul 23 18:50:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* array.c (Array#empty?), Hash.c (Hash#empty?), ext/dbm/dbm.c (DBM#empty?):
空の判定述語
* eval.c (f_unless): ifの逆をするイテレータ
* eval.c (f_until): whileの逆をするイテレータ
* parse.y: notの優先順位をand/orより高く
* parse.y (expr): `!'を引数括弧を省略したcallでも有効に
Mon Jul 22 10:15:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99-960722
* array.c (ary_print_on): OFSのNILチェックが不完全
* ruby.c (load_file): 標準入力からのスクリプトが空の時に対応.
* ruby.c (proc_options): -wでは引数無しの時には標準入力からスクリ
プトをとる(-vではたんに終了する).
* array.c (ary_compact): nilの要素を取り除くメソッド
* array.c (ary_nitems): nilでない要素を数えるメソッド
Sun Jul 20 00:51:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ruby.c (proc_options): -w optionを追加
* parse.y: {}が閉じていない時には展開しない文字列を
Fri Jul 19 16:16:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99-960719
* lib/find.rb: 石塚版(pruneの拡張付き)
* file.c (test_l): lstatで調べないとね.
* eval.c (f_throw): 第2引数を省略可能に.
* parse.y (str_extend): {}のネストに対応
Thu Jul 18 18:25:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99-960718
* parse.y (str_extend): 文字列中の式展開に \" ' ` / を含む事ができ
るように.
Tue Jul 16 15:55:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-parse-region): 正規表現内のエスケープ
に対応
* version 0.99-960716
Fri Jul 12 10:06:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* io.c (f_select): 引数のclose check.
* ruby.c (load_file): #!行の引数チェックを第1引数に限定(実をいうと
DOS改行対策)
Wed Jul 10 17:18:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99-960710
* time.c (time_s_timegm/time_s_timelocal): 時間を生成するメソッド
Mon Jun 17 15:59:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99-960617
* parse.y (yyerror): エラー表示の簡略化.
Wed Jun 12 14:11:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* signal.c (rb_trap_exit): trap 0はthreadを生成せずに処理する.
Fri Jun 7 10:17:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* array.c/hash.c (indexes): 配列1引数のパターンを無くした.配列の
場合は`*ary'を使ってもらおう.
* eval.c (thread_wait_threads): main_threadが終了する前に他の
threadを待つ(強制的には終了させない).
(ruby_run): 他のthreadを待っている間にシグナルが来たら,全thread
を強制終了させる.
* eval.c (rb_fail): メソッド名を`$!'に埋め込む.
* eval.c (thread_create): main_threadのコンテクストがセーブされな
い場合があった.
* process.c (f_sleep): 時間を指定せず,threadがひとつしかない状況
にも対応.
* eval.c (thread_create): create後,fnを呼び出す前にcontext switch
が起きると違うcontextでfnが実行されてしまうバグ.
Mon Jun 3 08:03:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* struct.c (struct_s_def): メンバの指定を文字列,シンボル(FIXNUM)
双方で可能にした.
* ext/etc/etc.c (Init_etc): 構造体オブジェクトをGCから保護した.
* error.c (rb_sys_fail): nil/FALSEを引数として受け付けるように.
Thu May 30 16:19:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_select): EINTRに対応.
Wed May 29 11:04:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (f_catch): catch/throwを実装した.
Tue May 28 13:30:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99-960528
* eval.c (thread_cleanup): main threadが終了すると他のthreadも終了
することの明確化.
* signal.c (trap): SIGINTのデフォルトの設定ミス(本当にSIG_DFLでは
まずかった).rubyではちゃんとハンドルしないと.
* eval.c (thread_interrupt): SIGINTはmain_threadに例外を発生させる
ように.
Mon May 27 15:13:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_status): threadの状態を返すメソッド.threadの終了
を待たない.
* eval.c (thread_value): 一種のpromiseを実装するためのメソッド.
* eval.c (thread_join): 待っているthreadが例外を起こした時には,
joinがその例外を発生するように.
* eval.c (thread_create): threadでの例外をpropagateしないように.
Fri May 24 10:47:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* enum.c (Init_Enumerable): `size' as alias to the `length'
* eval.c (thread_save_context): `$@', `$!'をスレッド毎にセーブ.
* eval.c (superclass): エラー表示をより親切に.
Thu May 23 10:38:41 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.99-960523
* eval.c (superclass): エラー時にスーパークラス名を(分かれば)表示
するように.
Wed May 22 19:48:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (superclass): スーパークラスの指定子を`:'から`<'に変更.
Tue May 21 09:27:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* lib/thread.rb: threadをサポートするクラス(Mutex, Queue).
Mon May 20 09:39:49 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* time.c (time_cmp): 浮動小数点数も扱えるように.
(time_minus): Time - Timeが浮動小数点数を返すように.
Fri May 17 15:40:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* process.c (rb_proc_exec): Thread対応時にexecの直前に
ITIMER_VIRTUALをリセットする.
Tue May 14 02:12:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* signal.c (sighandle): SIGINTに対してデフォルトで例外を発生させる
のをやめ,status 130でexitするようにした.
* eval.c (thread_schedule): Threadのバグはほとんどとれたようだ.
Fri May 10 11:21:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (thread_schedule): ユーザレベルThread機能.効率はともかく
移植性はある.今後,thread間の通信機能を実装する予定.
Thu May 2 21:22:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* time.c (time_timeval): struct timevalを直接返すように(static変数
を使わない).
Wed May 1 17:27:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* process.c (f_sleep): 整数以外のtimeを指定できるように.
Thu Apr 25 08:19:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* file.c (file_s_dirname): ファイル名が"/"を含まない時,"."を返す
ように(GNU dirnameの仕様).
* file.c (file_s_basename): まだnilと0を混同しているソースが残って
いた.
* parse.y (exprs): エラーリカバリを追加.
Wed Apr 24 15:51:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_chop_bang): CRLFの場合2 bytesをchop!するように.
* ext/socket/socket.c (tcp_svr_s_open): まだnilと0を混同しているソー
スが残っていた.
Tue Apr 23 18:14:25 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* pack.c (pack_pack): "A/a"のバグ.余計なpaddingが入っていた.
Thu Apr 18 13:02:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* configure.in: アーキテクチャ依存部を別ディレクトリにインストール
するように.
* parse.y (yyerror): エラー発生時にエラー行とその位置を表示するよ
うに.
Wed Apr 17 14:22:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* defines.h: SAFE_SIGHANDLEを無くし,危険な選択はできないように.
* io.c (io_ungetc): 新機能.
* ruby.c (load_file): ファイルからの読み込み方式が変わったのに対応.
* parse.y (compile_file): ファイルからの入力を一度全部読み込むのを
止めて,getsを使うことにした.
Wed Apr 10 17:40:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.98
Tue Apr 9 09:54:30 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (iter_block): イテレータブロックの指定をメソッド呼び出し
に限定.文法の明確化.
* eval.c (rb_eval): 条件式の正規表現の比較をinline化.
* eval.c (rb_eval): defined? の 定義情報(種別)を文字列で返す.
* node.h: NODE_BEGIN -> NODE_RESCUE, NODE_ENSUREに分離.
* eval.c (rb_eval): option -n/-pのトップレベルループのinline展開.
* parse.y (cond0): 条件式中の文字列は比較の対象としない
Wed Mar 27 12:33:54 1996 Tairo Nomura <tairo@hucom.tp.titech.ac.jp>
* defines.h: NeXT対応
Wed Mar 27 10:02:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y: 予約語の変更 continue -> next
Mon Mar 25 07:34:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (parse_regx): o(once)オプションを追加.
Fri Mar 22 14:25:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.97d
* eval.c (dyna_var_defined): 動的ローカル変数の定義チェック用ルー
チン.
* parse.y (gettable): eval()の中での動的ローカル変数(既に値を持っ
ているもの)の検出に失敗していた.
Tue Mar 19 10:46:47 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.97c
* re.c (reg_s_new): compile時にsegmentation fault.
* parse.y (str_extend): いつもevalするように.
Wed Mar 13 11:00:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (str_extend): 文字列中の式展開の不備を無くした.
* parse.y: 下手なエラーリカバリを外した.
Tue Mar 12 12:30:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rescue): 間違ってensureでも例外を捕捉していた.
Wed Mar 6 12:11:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* parse.y (var_extend): 変数展開"#{}"で,任意の式を書けるようにし
た,これで「変数」展開では無くなっちゃったなあ.
* regex.c (init_syntax_once): `_'をwordに追加.
* regex.c (re_compile_pattern): `\w',`\W'の判定をsyntax tableを使
うように.
Tue Feb 27 10:15:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* object.c (obj_inspect): 表示するインスタンス変数が無い時には,
to_sを使う.
* configure.in: dlnの検出を自動的に.
Mon Feb 26 19:55:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ruby.c (readin): read(2)で一度にファイルが読み込めない場合に対応.
Sat Feb 24 14:47:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.97b
Fri Feb 23 11:26:02 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* class.c (rb_define_module): C言語で定義されたモジュールのPATHの
設定忘れ.文字列化でcore dump.
* eval.c (mod_include): 戻り値をnilに.
* version 0.97a
Thu Feb 22 21:03:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* array.c (ary_times): 「配列*文字列」がjoinと同じ働きをするように.
Wed Feb 21 11:18:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* configure.in : fileCountをcache.
* configure.in : LinuxでELF環境を自動的に検出できるよう.
Tue Feb 20 11:18:09 1996 Mitsuhide Satou <mit-sato@aries.bekkoame.or.jp>
* FreeBSD dynamic link対応.
Fri Feb 16 08:50:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* object.c (obj_inspect): インスタンス変数を持たないオブジェクトも
正しく表示されるように.
Wed Feb 14 16:56:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_eval): 条件式の`2..2'など左辺成立直後に右辺が成立する
パターンにバグ.
Tue Feb 13 18:22:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.97
Fri Feb 9 21:32:55 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* lib/tkscrollbox.rb: スクロールでtclの設定を行い,ruby<->wishの不
要な通信を無くした.
Wed Feb 7 10:26:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_aref): indexをunsigned intでとっていた.
* string.c (str_aref): 範囲外のindexに対してnilを返す.
* parse.y (special_local_set): `$_'が宣言無しに使われた場合に対応.
関数をvariable.cから移動.
* string.c (str_sub): 置換開始位置が間違っていた.
Tue Feb 6 16:17:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-parse-region): コメントの読み飛ばしの
バグ.
Fri Feb 2 18:35:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* variable.c (lastline_get): `$_'を`$~'と同じようにSCOPEローカルな
変数にした.
Thu Feb 1 14:14:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* file.c: statのcacheをやめた.
Wed Jan 31 07:13:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (proc_s_new): procの中でyieldを呼ばれた時にcore dumpして
いた.とりあえず例外を発生させる.
* variable.c (rb_class2path): singleton classに対応.
* ext/etc/etc.c (Init_etc): struct_defineのターミネータがnilだった
(0でなければならない).
* ext/marshal/marshal.c: TRUE/FALSEを吐き出せるように.
* eval.c (rb_get_method_body): キャッシュのalias対応,いままでは
aliasはキャッシュに入っていなかった.
Tue Jan 30 09:55:13 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_eval): NODE_BLOCK - tail recursive(というほどでもない
が).
* io.c (io_pipe): pipe(2)を実装した.
* eval.c (rb_eval): Qselfをなくした.thread対応への第一歩.先は遠
いが….
* eval.c (proc_call): procの中でのreturnはprocの終了を意味するよう
に.ただし,procからのyieldの中でのreturnは例外を発生する.
Wed Jan 24 11:33:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.96a
* dir.c (dir_each): `$_'の値を変更するのをやめた.
* io.c (f_readlines): nilとFALSEの分離のあおりで無限ループに落ちて
いた.
* ruby.c (ruby_options): $0の設定ミス.
Tue Jan 23 15:28:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_eval): ``は文字列を引数とするメソッド(`)呼び出しのシ
ンタックスシュガーであるとした.
* ruby.c (addpath): `-I'オプションでディレクトリが「前に」追加され
るように変更.
Fri Jan 19 11:23:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* dln.c (load_1): N_INDR対応(出来たような気がする).
Thu Jan 18 18:14:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* ruby.texi: FALSEとnilの分離を反映した.
Tue Jan 16 17:39:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.96 - とりあえずnilとFALSEを区別する版
Wed Jan 10 15:31:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* re.c (reg_match): マッチしなかった時の戻り値はFALSE.
* object.c (rb_equal): `0 == nil'がTRUEになるバグ.
Tue Jan 9 00:44:58 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* nilとFALSEが分離可能に変更.
* nilとFALSEと0の区別を厳密に.
* struct.c (struct_new): 引数を0で終る必要が無くなった.
* object.c (inspect_i): オブジェクトのチェックのバグ(Fixnumでcore
dumpしていた).
* range.c (range_to_s): Rangeの表示を改善.
* object.c (true_inspect): TRUEの表示を`TRUE'に.
Mon Jan 8 15:02:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
* numeric.c (fix_mul): divide by zero errorが発生した(オーバーフロー
検出のバグ)
* texinfo.texをパッケージに含めた.
Sun Dec 31 00:08:49 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_eval): `::'では,そのクラスで定義された定数を参照する
ように変更.
* string.c (Init_String): eachをeach_lineに戻した.
Thu Dec 28 12:31:55 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* eval.c (rb_eval): caseの演算子を`=~'から`==='に.
* variable.c (rb_const_set): クラス定数の再定義を許す(同じクラスで
は不可).警告は出す.
Wed Dec 27 13:27:52 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.95c
* ext/tkutil/tkutil.c: wishがあってもなくても一応コンパイルだけは
するように.
* lib/tk.rb: 環境変数PATHから{wish|wish4.0}を探すように.
Tue Dec 26 01:03:42 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* sample/ruby-mode.el (ruby-parse-region): 正規表現の検出強化.
* numeric.c (fix_mul): 乗算のオーバーフロー検出アルゴリズムのバグ.
* ext/extmk.rb.in: ./install-shを使う場合のPATHを調整.
* Makefile.in (install): lib/*.rbを一つずつインストール.
* io.c (io_each_line): イテレータの戻り値をnilで統一.
Fri Dec 22 10:34:32 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* version 0.95b
* variable.c (f_untrace_var): 第2引数を指定すると特定のtraceを削除
できるように.
* variable.c (f_trace_var): 第2引数がnilの時,traceを削除する.
* lib/tk.rb (file_readable/file_writable): 第2引数をnilにすること
によるevent handlerの削除.
* parse.y (variable): ドキュメントに`__FILE__'と`__LINE__'が残って
いた.`caller(0)'で代用したはずだったのに.
* eval.c (f_eval): $!のリセット.
* error.c (err_sprintf): 勝手に"\n"を付加するのを止めた.
* parse.y (f_arglist): 引数リスト直後のif/whileの読み間違い.
lex_stateの値が設定されていなかった.
Co-authored-by: Jun Kuroda <j_kuro@pluto.ai.kutech.ac.jp>
Co-authored-by: Mitsuhide Satou <mit-sato@aries.bekkoame.or.jp>
Co-authored-by: SHIROYAMA Takayuki <psi@fortune.nest.or.jp>
Co-authored-by: Tairo Nomura <tairo@hucom.tp.titech.ac.jp>
Co-authored-by: WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Co-authored-by: Yasuo OHBA <jammy@shljapan.co.jp>
Diffstat (limited to 'eval.c')
-rw-r--r-- | eval.c | 3252 |
1 files changed, 2419 insertions, 833 deletions
@@ -11,8 +11,8 @@ ************************************************/ #include "ruby.h" -#include "env.h" #include "node.h" +#include "env.h" #include "sig.h" #include <stdio.h> @@ -23,30 +23,21 @@ #ifdef HAVE_STRING_H # include <string.h> #else -char *strchr(); char *strrchr(); #endif -VALUE cProc; +static VALUE cProc; static VALUE proc_call(); -static void rb_clear_cache_body(); -static void rb_clear_cache_entry(); - -/* #define TEST /* prints cache miss */ -#ifdef TEST -#include <stdio.h> -#endif - #define CACHE_SIZE 0x200 #define CACHE_MASK 0x1ff #define EXPR1(c,m) ((((int)(c)>>3)^(m))&CACHE_MASK) struct cache_entry { /* method hash table. */ ID mid; /* method's id */ + ID mid0; /* method's original id */ struct RClass *class; /* receiver's class */ struct RClass *origin; /* where method defined */ - int nargs; /* # of args */ NODE *method; int noex; }; @@ -62,14 +53,7 @@ rb_add_method(class, mid, node, noex) { NODE *body; - if (class == Qnil) class = (struct RClass*)cObject; - if (st_lookup(class->m_tbl, mid, &body)) { - Warning("redefine %s", rb_id2name(mid)); - rb_clear_cache_body(body); - } - else { - rb_clear_cache_entry(class, mid); - } + if (NIL_P(class)) class = (struct RClass*)cObject; body = NEW_METHOD(node, noex); st_insert(class->m_tbl, mid, body); } @@ -83,7 +67,7 @@ search_method(class, id, origin) while (!st_lookup(class->m_tbl, id, &body)) { class = class->super; - if (class == Qnil) return Qnil; + if (!class) return 0; } if (origin) *origin = class; @@ -102,29 +86,25 @@ rb_get_method_body(classp, idp, noexp) struct RClass *origin; struct cache_entry *ent; - if ((body = search_method(class, id, &origin)) == FALSE) { - return Qnil; + if ((body = search_method(class, id, &origin)) == 0) { + return 0; } - if (body->nd_body == Qnil) return Qnil; + if (!body->nd_body) return 0; - ent = cache + EXPR1(class, id); -#ifdef TEST - if (ent->mid != 0) { - fprintf(stderr, "0x%x 0x%x %x\n", class, id, EXPR1(class, id)); - } -#endif /* store in cache */ + ent = cache + EXPR1(class, id); ent->class = class; ent->noex = body->nd_noex; body = body->nd_body; if (nd_type(body) == NODE_FBODY) { + ent->mid = id; *classp = ent->origin = (struct RClass*)body->nd_orig; - *idp = ent->mid = body->nd_mid; + *idp = ent->mid0 = body->nd_mid; body = ent->method = body->nd_head; } else { *classp = ent->origin = origin; - ent->mid = id; + ent->mid = ent->mid0 = id; ent->method = body; } @@ -142,17 +122,9 @@ rb_alias(class, name, def) if (name == def) return; body = search_method(class, def, &origin); - if (body == Qnil || body->nd_body == Qnil) { - Fail("undefined method `%s' for class `%s'", - rb_id2name(def), rb_class2name(class)); - } - - if (st_lookup(class->m_tbl, name, &old)) { - Warning("redefine %s", rb_id2name(name)); - rb_clear_cache_body(body); - } - else { - rb_clear_cache_entry(class, name); + if (!body || !body->nd_body) { + NameError("undefined method `%s' for class `%s'", + rb_id2name(def), rb_class2name(class)); } st_insert(class->m_tbl, name, @@ -170,9 +142,9 @@ rb_export_method(class, name, noex) struct RClass *origin; body = search_method(class, name, &origin); - if (body == Qnil) { - Fail("undefined method `%s' for class `%s'", - rb_id2name(name), rb_class2name(class)); + if (!body) { + NameError("undefined method `%s' for class `%s'", + rb_id2name(name), rb_class2name(class)); } if (body->nd_noex != noex) { if (class == origin) { @@ -200,85 +172,56 @@ method_boundp(class, id, ex) return FALSE; } -VALUE +int rb_method_boundp(class, id) struct RClass *class; ID id; { - return method_boundp(class, id, 0); -} - -static void -rb_clear_cache_body(body) - NODE *body; -{ - struct cache_entry *ent, *end; - - ent = cache; end = ent + CACHE_SIZE; - while (ent < end) { - if (ent->method == body) { - ent->class = Qnil; - ent->mid = Qnil; - } - ent++; - } -} - -static void -rb_clear_cache_entry(class, mid) - struct RClass *class; - ID mid; -{ - struct cache_entry *ent; - - /* is it in the method cache? */ - ent = cache + EXPR1(class, mid); - if (ent->mid == mid && ent->class == class) { - ent->class = Qnil; - ent->mid = Qnil; - } + if (method_boundp(class, id, NOEX_PRIVATE)) + return TRUE; + return FALSE; } void -rb_clear_cache(class) - struct RClass *class; +rb_clear_cache() { struct cache_entry *ent, *end; ent = cache; end = ent + CACHE_SIZE; while (ent < end) { - if (ent->origin == class) { - ent->class = Qnil; - ent->mid = Qnil; - } + ent->mid = 0; ent++; } } -static ID match, each, aref, aset; -VALUE errstr, errat; +static ID init, eqq, each, aref, aset; +VALUE errinfo = Qnil, errat = Qnil; extern NODE *eval_tree; extern int nerrs; -extern VALUE TopSelf; -VALUE Qself; - -#define PUSH_SELF(s) { \ - VALUE __saved_self__ = Qself; \ - Qself = s; \ +extern VALUE cKernel; +extern VALUE eGlobalExit; +extern VALUE eInterrupt; +extern VALUE eSystemExit; +extern VALUE eException; +extern VALUE eRuntimeError; +extern VALUE eSyntaxError; +static VALUE eLocalJumpError; -#define POP_SELF() Qself = __saved_self__; } +extern VALUE TopSelf; struct FRAME *the_frame; struct SCOPE *the_scope; static struct FRAME *top_frame; -static struct SCOPE *top_scope; +static struct SCOPE *top_scope; #define PUSH_FRAME() { \ struct FRAME _frame; \ _frame.prev = the_frame; \ _frame.file = sourcefile; \ _frame.line = sourceline; \ + _frame.iter = the_iter->iter; \ + _frame.cbase = the_frame->cbase; \ the_frame = &_frame; \ #define POP_FRAME() the_frame = _frame.prev; } @@ -289,6 +232,7 @@ struct BLOCK { VALUE self; struct FRAME frame; struct SCOPE *scope; + struct RClass *class; int level; int iter; struct RVarmap *d_vars; @@ -297,24 +241,29 @@ struct BLOCK { #define PUSH_BLOCK(v,b) { \ struct BLOCK _block; \ - _block.level = tag_level; \ + _block.level = (int)&prot_tag; \ _block.var = v; \ _block.body = b; \ - _block.self = Qself; \ + _block.self = self; \ _block.frame = *the_frame; \ + _block.class = the_class; \ _block.frame.file = sourcefile; \ _block.frame.line = sourceline; \ _block.scope = the_scope; \ _block.d_vars = the_dyna_vars; \ _block.prev = the_block; \ - _block.iter = iter->iter; \ + _block.iter = the_iter->iter; \ the_block = &_block; \ #define PUSH_BLOCK2(b) { \ - b->prev = the_block; \ - the_block = b; \ - -#define POP_BLOCK() the_block = the_block->prev; } + struct BLOCK _block; \ + _block = *b; \ + _block.prev = the_block; \ + the_block = &_block; + +#define POP_BLOCK() \ + the_block = the_block->prev; \ +} struct RVarmap *the_dyna_vars; #define PUSH_VARS() { \ @@ -324,13 +273,28 @@ struct RVarmap *the_dyna_vars; #define POP_VARS() the_dyna_vars = _old; } VALUE +dyna_var_defined(id) + ID id; +{ + struct RVarmap *vars = the_dyna_vars; + + while (vars && vars->id) { + if (vars->id == id) return TRUE; + vars = vars->next; + } + return FALSE; +} + +VALUE dyna_var_ref(id) ID id; { struct RVarmap *vars = the_dyna_vars; - while (vars) { - if (vars->id == id) return vars->val; + while (vars && vars->id) { + if (vars->id == id) { + return vars->val; + } vars = vars->next; } return Qnil; @@ -343,16 +307,16 @@ dyna_var_asgn(id, value) { struct RVarmap *vars = the_dyna_vars; - while (vars) { + while (vars && vars->id) { if (vars->id == id) { vars->val = value; - return; + return value; } vars = vars->next; } { NEWOBJ(_vars, struct RVarmap); - OBJSETUP(_vars, Qnil, T_VARMAP); + OBJSETUP(_vars, 0, T_VARMAP); _vars->id = id; _vars->val = value; _vars->next = the_dyna_vars; @@ -360,11 +324,22 @@ dyna_var_asgn(id, value) } return value; } - + +static VALUE +dyna_var_mark() +{ + NEWOBJ(_vars, struct RVarmap); + OBJSETUP(_vars, 0, T_VARMAP); + _vars->id = 0; + _vars->val = Qnil; + _vars->next = the_dyna_vars; + the_dyna_vars = _vars; +} + static struct iter { int iter; struct iter *prev; -} *iter; +} *the_iter; #define ITER_NOT 0 #define ITER_PRE 1 @@ -372,20 +347,16 @@ static struct iter { #define PUSH_ITER(i) { \ struct iter _iter; \ - _iter.prev = iter; \ + _iter.prev = the_iter; \ _iter.iter = (i); \ - iter = &_iter; \ + the_iter = &_iter; \ #define POP_ITER() \ - iter = _iter.prev; \ + the_iter = _iter.prev; \ } -static int tag_level, target_level; static struct tag { - int level; jmp_buf buf; - struct gc_list *gclist; - VALUE self; struct FRAME *frame; struct iter *iter; struct tag *prev; @@ -393,57 +364,50 @@ static struct tag { #define PUSH_TAG() { \ struct tag _tag; \ - _tag.level= ++tag_level; \ - _tag.self = Qself; \ _tag.frame = the_frame; \ - _tag.iter = iter; \ + _tag.iter = the_iter; \ _tag.prev = prot_tag; \ - prot_tag = &_tag; \ + prot_tag = &_tag; -#define EXEC_TAG() (setjmp(prot_tag->buf)) +#define EXEC_TAG() ((NODE*)setjmp(prot_tag->buf)) -#define JUMP_TAG(val) { \ - Qself = prot_tag->self; \ +#define JUMP_TAG(st) { \ the_frame = prot_tag->frame; \ - iter = prot_tag->iter; \ - longjmp(prot_tag->buf,(val)); \ + the_iter = prot_tag->iter; \ + longjmp(prot_tag->buf,(int)(st)); \ } +#define JUMP_TAG3(val,data1,data2) \ + JUMP_TAG(newnode(NODE_TAG,(val),(data1),(data2))) + +#define JUMP_TAG2(val,data) JUMP_TAG3((val),(data),0) + #define POP_TAG() \ - tag_level--; \ prot_tag = _tag.prev; \ } -#define TAG_RETURN 1 -#define TAG_BREAK 2 -#define TAG_CONTINUE 3 -#define TAG_RETRY 4 -#define TAG_REDO 5 -#define TAG_FAIL 6 -#define TAG_EXIT 7 +#define TAG_RETURN 0x1 +#define TAG_BREAK 0x2 +#define TAG_NEXT 0x3 +#define TAG_RETRY 0x4 +#define TAG_REDO 0x5 +#define TAG_RAISE 0x6 +#define TAG_THROW 0x7 +#define TAG_FATAL 0x8 -#define IN_BLOCK 0x08 +#define IN_BLOCK 0x10 struct RClass *the_class; -struct class_link { - struct RClass *class; - struct class_link *prev; -} *class_link; #define PUSH_CLASS() { \ - struct class_link _link; \ - _link.class = the_class; \ - _link.prev = class_link; \ - class_link = &_link \ + struct RClass *_class = the_class; \ -#define POP_CLASS() \ - the_class = class_link->class; \ - class_link = _link.prev; } +#define POP_CLASS() the_class = _class; } #define PUSH_SCOPE() { \ struct SCOPE *_old; \ NEWOBJ(_scope, struct SCOPE); \ - OBJSETUP(_scope, Qnil, T_SCOPE); \ + OBJSETUP(_scope, 0, T_SCOPE); \ _old = the_scope; \ the_scope = _scope; \ @@ -452,54 +416,96 @@ struct class_link { the_scope->local_vars = 0;\ the_scope->local_tbl = 0;\ }\ + the_scope->flag |= SCOPE_NOSTACK;\ the_scope = _old;\ } static VALUE rb_eval(); -static VALUE f_eval(); +static VALUE eval(); +static NODE *compile(); static VALUE rb_call(); VALUE rb_apply(); -VALUE rb_xstring(); -void rb_fail(); - -VALUE rb_rescue(); static void module_setup(); static VALUE masign(); static void asign(); -static VALUE last_val; - extern VALUE rb_stderr; extern int sourceline; extern char *sourcefile; -static ID last_func; static void -error_print(last_func) - ID last_func; +error_pos() { - if (errat) { - fwrite(RSTRING(errat)->ptr, 1, RSTRING(errat)->len, stderr); - if (last_func) { - fprintf(stderr, ":in `%s': ", rb_id2name(last_func)); + if (sourcefile) { + if (the_frame->last_func) { + fprintf(stderr, "%s:%d:in `%s': ", sourcefile, sourceline, + rb_id2name(the_frame->last_func)); } else { - fprintf(stderr, ": "); + fprintf(stderr, "%s:%d: ", sourcefile, sourceline); } } +} - if (errstr) { - fwrite(RSTRING(errstr)->ptr, 1, RSTRING(errstr)->len, stderr); - if (RSTRING(errstr)->ptr[RSTRING(errstr)->len - 1] != '\n') { - putc('\n', stderr); +static void +error_print(state) + NODE *state; +{ + struct FRAME *frame = the_frame; + VALUE etype; + + if (!NIL_P(errat)) { + VALUE mesg; + + switch (TYPE(errat)) { + case T_STRING: + mesg = errat; + errat = Qnil; + break; + case T_ARRAY: + mesg = RARRAY(errat)->ptr[0]; + break; } + fwrite(RSTRING(mesg)->ptr, 1, RSTRING(mesg)->len, stderr); + fprintf(stderr, ": "); + } + + etype = rb_class_path(CLASS_OF(errinfo)); + + if (verbose) { + fwrite(RSTRING(etype)->ptr, 1, RSTRING(etype)->len, stderr); + putc('|', stderr); + } + if (RSTRING(errinfo)->len == 0) { + fprintf(stderr, "unhandled exception.\n"); } else { - fprintf(stderr, "unhandled failure.\n"); + fwrite(RSTRING(errinfo)->ptr, 1, RSTRING(errinfo)->len, stderr); + if (RSTRING(errinfo)->ptr[RSTRING(errinfo)->len - 1] != '\n') { + putc('\n', stderr); + } + } + + if (!NIL_P(errat)) { + int i; + struct RArray *ep = RARRAY(errat); + +#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5) +#define TRACE_HEAD 8 +#define TRACE_TAIL 5 + + for (i=1; i<ep->len; i++) { + fprintf(stderr, "\tfrom %s\n", RSTRING(ep->ptr[i])->ptr); + if (i == TRACE_HEAD && ep->len > TRACE_MAX) { + fprintf(stderr, "\t ... %d levels...\n", + ep->len - TRACE_HEAD - TRACE_TAIL); + i = ep->len - TRACE_TAIL; + } + } } } @@ -511,9 +517,12 @@ ruby_init(argc, argv, envp) int argc; char **argv, **envp; { - int state; static struct FRAME frame; + static struct iter iter; + NODE *state; + the_frame = top_frame = &frame; + the_iter = &iter; origenviron = environ; #ifdef NT @@ -526,144 +535,171 @@ ruby_init(argc, argv, envp) the_scope->local_tbl = 0; top_scope = the_scope; - PUSH_TAG(); - PUSH_ITER(ITER_NOT); + PUSH_TAG() if ((state = EXEC_TAG()) == 0) { rb_call_inits(); the_class = (struct RClass*)cObject; + the_frame->cbase = (VALUE)newnode(NODE_CREF,cObject,0,0); ruby_options(argc, argv, envp); } - POP_ITER(); POP_TAG(); + if (state) error_print(state); POP_SCOPE(); the_scope = top_scope; - - if (state == TAG_EXIT) { - rb_trap_exit(); - exit(FIX2UINT(last_val)); - } - if (state) { - error_print(last_func); - } } static VALUE -Eval() +eval_node(self) + VALUE self; { VALUE result = Qnil; NODE *tree; - int state; - if (!eval_tree) return Qnil; + if (!eval_tree) return FALSE; tree = eval_tree; eval_tree = 0; - result = rb_eval(tree); + result = rb_eval(self, tree); return result; } +int rb_in_eval; + +#ifdef THREAD +static void thread_cleanup(); +static void thread_wait_other_threads(); +#endif + +static int exit_status; + void ruby_run() { - int state; + NODE *state; + static NODE *ex; if (nerrs > 0) exit(nerrs); init_stack(); - rb_define_variable("$!", &errstr); errat = Qnil; /* clear for execution */ PUSH_TAG(); PUSH_ITER(ITER_NOT); if ((state = EXEC_TAG()) == 0) { - Eval(); + eval_node(TopSelf); + } + POP_ITER(); + POP_TAG(); + + if (state && !ex) ex = state; + PUSH_TAG(); + PUSH_ITER(ITER_NOT); + if ((state = EXEC_TAG()) == 0) { rb_trap_exit(); +#ifdef THREAD + thread_cleanup(); + thread_wait_other_threads(); +#endif + } + else { + ex = state; } POP_ITER(); POP_TAG(); - switch (state) { - case 0: - break; + if (!ex) { + exit(0); + } + + switch (ex->nd_tag) { case TAG_RETURN: - Fatal("unexpected return"); + error_pos(); + fprintf(stderr, "unexpected return\n"); + exit(1); break; - case TAG_CONTINUE: - Fatal("unexpected continue"); + case TAG_NEXT: + error_pos(); + fprintf(stderr, "unexpected next\n"); + exit(1); break; case TAG_BREAK: - Fatal("unexpected break"); + error_pos(); + fprintf(stderr, "unexpected break\n"); + exit(1); break; case TAG_REDO: - Fatal("unexpected redo"); + error_pos(); + fprintf(stderr, "unexpected redo\n"); + exit(1); break; case TAG_RETRY: - Fatal("retry outside of protect clause"); + error_pos(); + fprintf(stderr, "retry outside of protect clause\n"); + exit(1); break; - case TAG_FAIL: - error_print(last_func); + case TAG_RAISE: + case TAG_FATAL: + if (obj_is_kind_of(errinfo, eSystemExit)) { + exit(exit_status); + } + error_print(ex); exit(1); break; - case TAG_EXIT: - exit(FIX2UINT(last_val)); + case TAG_THROW: + error_pos(); + fprintf(stderr, "uncaught throw `%s'\n", rb_id2name(ex->nd_tlev)); + exit(1); break; default: - Bug("Unknown longjmp status %d", state); + Bug("Unknown longjmp status %d", ex->nd_tag); break; } - exit(0); } static void -syntax_error() +compile_error(at) + char *at; { VALUE mesg; - mesg = errstr; + mesg = errinfo; nerrs = 0; - errstr = str_new2("syntax error in eval():\n"); - str_cat(errstr, RSTRING(mesg)->ptr, RSTRING(mesg)->len); - rb_fail(errstr); + errinfo = exc_new(eSyntaxError, "compile error in "); + str_cat(errinfo, at, strlen(at)); + str_cat(errinfo, ":\n", 2); + str_cat(errinfo, RSTRING(mesg)->ptr, RSTRING(mesg)->len); + rb_raise(errinfo); } VALUE rb_eval_string(str) char *str; { + VALUE v; char *oldsrc = sourcefile; - lex_setsrc("(eval)", str, strlen(str)); - eval_tree = 0; - PUSH_VARS(); - yyparse(); - POP_VARS(); + sourcefile = "(eval)"; + v = eval(TopSelf, str_new2(str), Qnil); sourcefile = oldsrc; - if (nerrs == 0) { - return Eval(); - } - else { - syntax_error(); - } - return Qnil; /* not reached */ + + return v; } void rb_eval_cmd(cmd, arg) VALUE cmd, arg; { - int state; + NODE *state; struct SCOPE *saved_scope; if (TYPE(cmd) != T_STRING) { - if (TYPE(cmd) == T_OBJECT - && obj_is_kind_of(cmd, cProc)) { + if (obj_is_kind_of(cmd, cProc)) { proc_call(cmd, arg); return; } } - PUSH_SELF(TopSelf); PUSH_CLASS(); PUSH_TAG(); saved_scope = the_scope; @@ -672,31 +708,29 @@ rb_eval_cmd(cmd, arg) the_class = (struct RClass*)cObject; if ((state = EXEC_TAG()) == 0) { - f_eval(Qself, cmd); + eval(TopSelf, cmd, Qnil); } the_scope = saved_scope; POP_TAG(); POP_CLASS(); - POP_SELF(); - switch (state) { - case 0: - break; + if (state == 0) return; + switch (state->nd_tag) { case TAG_RETURN: - Fatal("unexpected return"); + Raise(eLocalJumpError, "unexpected return"); break; - case TAG_CONTINUE: - Fatal("unexpected continue"); + case TAG_NEXT: + Raise(eLocalJumpError, "unexpected next"); break; case TAG_BREAK: - Fatal("unexpected break"); + Raise(eLocalJumpError, "unexpected break"); break; case TAG_REDO: - Fatal("unexpected redo"); + Raise(eLocalJumpError, "unexpected redo"); break; case TAG_RETRY: - Fatal("retry outside of protect clause"); + Raise(eLocalJumpError, "retry outside of protect clause"); break; default: JUMP_TAG(state); @@ -709,8 +743,7 @@ rb_trap_eval(cmd, sig) VALUE cmd; int sig; { -#ifdef SAFE_SIGHANDLE - int state; + NODE *state; PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { @@ -721,9 +754,74 @@ rb_trap_eval(cmd, sig) trap_immediate = 0; JUMP_TAG(state); } -#else - rb_eval_cmd(cmd, ary_new3(1, INT2FIX(sig))); -#endif +} + +static VALUE +superclass(self, node) + VALUE self; + NODE *node; +{ + VALUE val; + NODE *state; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + val = rb_eval(self, node); + } + POP_TAG(); + if ((state && state->nd_tag == TAG_RAISE) || !val || TYPE(val) != T_CLASS){ + switch (nd_type(node)) { + case NODE_COLON2: + TypeError("undefined superclass `%s'", rb_id2name(node->nd_mid)); + case NODE_CVAR: + case NODE_CONST: + TypeError("undefined superclass `%s'", rb_id2name(node->nd_vid)); + } + if (state) JUMP_TAG(state); + TypeError("superclass undefined"); + } + if (state) JUMP_TAG(state); + + return val; +} + +static VALUE +ev_const_defined(cref, id) + NODE *cref; + ID id; +{ + NODE *cbase = cref; + + while (cbase && cbase->nd_clss != cObject) { + struct RClass *class = RCLASS(cbase->nd_clss); + + if (class->iv_tbl && + st_lookup(class->iv_tbl, id, 0)) { + return TRUE; + } + cbase = cbase->nd_next; + } + return rb_const_defined(cref->nd_clss, id); +} + +static VALUE +ev_const_get(cref, id) + NODE *cref; + ID id; +{ + NODE *cbase = cref; + VALUE result; + + while (cbase && cbase->nd_clss != cObject) { + struct RClass *class = RCLASS(cbase->nd_clss); + + if (class->iv_tbl && + st_lookup(class->iv_tbl, id, &result)) { + return result; + } + cbase = cbase->nd_next; + } + return rb_const_get(cref->nd_clss, id); } #define SETUP_ARGS {\ @@ -739,9 +837,11 @@ rb_trap_eval(cmd, sig) n = node->nd_args;\ argv = ALLOCA_N(VALUE,argc);\ for (i=0;i<argc;i++) {\ - argv[i] = rb_eval(n->nd_head);\ + argv[i] = rb_eval(self,n->nd_head);\ n=n->nd_next;\ }\ + sourceline = nd_line(node);\ + sourcefile = node->file;\ }\ else {\ argc = 0;\ @@ -749,73 +849,111 @@ rb_trap_eval(cmd, sig) }\ }\ else {\ - VALUE args = rb_eval(n);\ + VALUE args = rb_eval(self,n);\ if (TYPE(args) != T_ARRAY)\ args = rb_to_a(args);\ argc = RARRAY(args)->len;\ argv = ALLOCA_N(VALUE, argc);\ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\ + sourceline = nd_line(node);\ + sourcefile = node->file;\ }\ } -#define RETURN(v) do { result = (v); goto finish; } while (0) +int +rb_test_false_or_nil(v) + VALUE v; +{ + return (v != Qnil) && (v != FALSE); +} + +static int handle_rescue(); +VALUE rb_yield_0(); static VALUE -rb_eval(node) +rb_eval(self, node) + VALUE self; register NODE *node; { - int state; + NODE *state; VALUE result = Qnil; +#define RETURN(v) { result = (v); goto finish; } + again: - if (node == Qnil) RETURN(Qnil); + if (!node) RETURN(Qnil); - sourceline = node->line; + sourceline = nd_line(node); sourcefile = node->file; switch (nd_type(node)) { case NODE_BLOCK: while (node) { - result = rb_eval(node->nd_head); + result = rb_eval(self, node->nd_head); node = node->nd_next; } break; + /* begin .. end without clauses */ + case NODE_BEGIN: + node = node->nd_body; + goto again; + + /* nodes for speed-up(default match) */ + case NODE_MATCH: + result = reg_match2(node->nd_head->nd_lit); + break; + + /* nodes for speed-up(top-level loop for -n/-p) */ + case NODE_OPT_N: + while (!NIL_P(f_gets())) { + rb_eval(self, node->nd_body); + } + RETURN(Qnil); + case NODE_SELF: - RETURN(Qself); + RETURN(self); case NODE_NIL: RETURN(Qnil); case NODE_IF: - if (rb_eval(node->nd_cond)) { + if (RTEST(rb_eval(self, node->nd_cond))) { node = node->nd_body; } else { node = node->nd_else; } - if (node) goto again; - RETURN(Qnil); + goto again; + + case NODE_UNLESS: + if (!RTEST(rb_eval(self, node->nd_cond))) { + node = node->nd_body; + } + else { + node = node->nd_else; + } + goto again; case NODE_CASE: { VALUE val; - val = rb_eval(node->nd_head); + val = rb_eval(self, node->nd_head); node = node->nd_body; while (node) { - if (nd_type(node) == NODE_WHEN) { - NODE *tag = node->nd_head; + NODE *tag; - while (tag) { - if (rb_funcall(rb_eval(tag->nd_head), match, 1, val)){ - RETURN(rb_eval(node->nd_body)); - } - tag = tag->nd_next; - } + if (nd_type(node) != NODE_WHEN) { + + RETURN(rb_eval(self, node)); } - else { - RETURN(rb_eval(node)); + tag = node->nd_head; + while (tag) { + if (RTEST(rb_funcall2(rb_eval(self, tag->nd_head),eqq,1,&val))){ + RETURN(rb_eval(self, node->nd_body)); + } + tag = tag->nd_next; } node = node->nd_next; } @@ -824,60 +962,62 @@ rb_eval(node) case NODE_WHILE: PUSH_TAG(); - switch (state = EXEC_TAG()) { - case 0: - while_cont: - while (rb_eval(node->nd_cond)) { + if ((state = EXEC_TAG()) == 0) { + while_next: + for (;;) { + if (node->nd_state && !RTEST(rb_eval(self, node->nd_cond))) + break; while_redo: - rb_eval(node->nd_body); + rb_eval(self, node->nd_body); + if (!node->nd_state && !RTEST(rb_eval(self, node->nd_cond))) + break; + } + } + else { + switch (state->nd_tag) { + case TAG_REDO: + state = 0; + goto while_redo; + case TAG_NEXT: + state = 0; + goto while_next; + default: + break; } - break; - case TAG_REDO: - state = 0; - goto while_redo; - case TAG_CONTINUE: - state = 0; - goto while_cont; - default: - break; } POP_TAG(); - switch (state) { - case 0: - case TAG_BREAK: - break; - default: + if (state && state->nd_tag != TAG_BREAK) { JUMP_TAG(state); - break; } RETURN(Qnil); - case NODE_WHILE2: + case NODE_UNTIL: PUSH_TAG(); - switch (state = EXEC_TAG()) { - case 0: - while2_cont: - do { - while2_redo: - rb_eval(node->nd_body); - } while (rb_eval(node->nd_cond)); - break; - case TAG_REDO: - state = 0; - goto while2_redo; - case TAG_CONTINUE: - state = 0; - goto while2_cont; - default: - case TAG_BREAK: - break; + if ((state = EXEC_TAG()) == 0) { + until_next: + for (;;) { + if (node->nd_state && RTEST(rb_eval(self, node->nd_cond))) + break; + until_redo: + rb_eval(self, node->nd_body); + if (!node->nd_state && RTEST(rb_eval(self, node->nd_cond))) + break; + } + } + else { + switch (state->nd_tag) { + case TAG_REDO: + state = 0; + goto while_redo; + case TAG_NEXT: + state = 0; + goto while_next; + default: + break; + } } POP_TAG(); - switch (state) { - case 0: - case TAG_BREAK: - break; - default: + if (state && state->nd_tag != TAG_BREAK) { JUMP_TAG(state); } RETURN(Qnil); @@ -885,6 +1025,8 @@ rb_eval(node) case NODE_ITER: case NODE_FOR: { + int tag_level; + iter_retry: PUSH_BLOCK(node->nd_var, node->nd_body); PUSH_TAG(); @@ -893,36 +1035,37 @@ rb_eval(node) if (state == 0) { if (nd_type(node) == NODE_ITER) { PUSH_ITER(ITER_PRE); - result = rb_eval(node->nd_iter); + result = rb_eval(self, node->nd_iter); POP_ITER(); } else { VALUE recv; - recv = rb_eval(node->nd_iter); + recv = rb_eval(self, node->nd_iter); PUSH_ITER(ITER_PRE); + sourceline = nd_line(node); + sourcefile = node->file; result = rb_call(CLASS_OF(recv),recv,each,0,0,0); POP_ITER(); } } POP_TAG(); + tag_level = the_block->level; POP_BLOCK(); - switch (state) { - case 0: - break; - + if (state == 0) break; + switch (state->nd_tag) { case TAG_RETRY: goto iter_retry; case IN_BLOCK|TAG_BREAK: - if (target_level != tag_level) { + if (state->nd_tlev != tag_level) { JUMP_TAG(state); } result = Qnil; break; case IN_BLOCK|TAG_RETURN: - if (target_level == tag_level) { - state &= ~IN_BLOCK; + if (state->nd_tlev == tag_level) { + state->nd_tag &= ~IN_BLOCK; } /* fall through */ default: @@ -931,79 +1074,91 @@ rb_eval(node) } break; - case NODE_FAIL: - { - VALUE mesg = rb_eval(node->nd_stts); - if (mesg) Check_Type(mesg, T_STRING); - rb_fail(mesg); - } - break; - case NODE_YIELD: - result = rb_yield(rb_eval(node->nd_stts)); + result = rb_yield_0(rb_eval(self, node->nd_stts), 0); break; - case NODE_BEGIN: - if (node->nd_resq == Qnil && node->nd_ensr == Qnil) { - node = node->nd_head; - goto again; + case NODE_RESCUE: + retry_entry: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(self, node->nd_head); } - else { - VALUE (*r_proc)(); - - if (node->nd_resq == (NODE*)1) { - r_proc = 0; - } - else { - r_proc = rb_eval; - } - if (node->nd_ensr) { - PUSH_TAG(); - if ((state = EXEC_TAG()) == 0) { - result = rb_rescue(rb_eval, node->nd_head, r_proc, node->nd_resq); - } - POP_TAG(); - /* ensure clause */ - rb_eval(node->nd_ensr); - if (state) { - JUMP_TAG(state); + POP_TAG(); + if (state) { + if (state->nd_tag == TAG_RAISE) { + NODE *resq = node->nd_resq; + while (resq) { + if (handle_rescue(self, resq)) { + state = 0; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(self, resq->nd_body); + } + POP_TAG(); + if (state == 0) { + errat = Qnil; + } + else if (state->nd_tag == TAG_RETRY) { + state = 0; + goto retry_entry; + } + break; + } + resq = resq->nd_head; /* next rescue */ } } - else { - result = rb_rescue(rb_eval, node->nd_head, r_proc, node->nd_resq); + if (state) { + JUMP_TAG(state); } } break; + case NODE_ENSURE: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(self, node->nd_head); + } + POP_TAG(); + rb_eval(self, node->nd_ensr); + if (state) { + JUMP_TAG(state); + } + break; + case NODE_AND: - if ((result = rb_eval(node->nd_1st)) == FALSE) RETURN(result); + result = rb_eval(self, node->nd_1st); + if (!RTEST(result)) break; node = node->nd_2nd; goto again; case NODE_OR: - if ((result = rb_eval(node->nd_1st)) != FALSE) RETURN(result); + result = rb_eval(self, node->nd_1st); + if (RTEST(result)) break; node = node->nd_2nd; goto again; case NODE_NOT: - if (rb_eval(node->nd_body)) result = FALSE; + if (RTEST(rb_eval(self, node->nd_body))) result = FALSE; else result = TRUE; break; case NODE_DOT2: case NODE_DOT3: - RETURN(range_new(rb_eval(node->nd_beg), rb_eval(node->nd_end))); + RETURN(range_new(rb_eval(self, node->nd_beg), rb_eval(self, node->nd_end))); case NODE_FLIP2: /* like AWK */ if (node->nd_state == 0) { - if (rb_eval(node->nd_beg)) { - node->nd_state = rb_eval(node->nd_end)?0:1; + if (RTEST(rb_eval(self, node->nd_beg))) { + node->nd_state = rb_eval(self, node->nd_end)?0:1; result = TRUE; } - result = FALSE; + else { + result = FALSE; + } } else { - if (rb_eval(node->nd_end)) { + if (RTEST(rb_eval(self, node->nd_end))) { node->nd_state = 0; } result = TRUE; @@ -1012,39 +1167,22 @@ rb_eval(node) case NODE_FLIP3: /* like SED */ if (node->nd_state == 0) { - if (rb_eval(node->nd_beg)) { + if (RTEST(rb_eval(self, node->nd_beg))) { node->nd_state = 1; result = TRUE; } result = FALSE; } else { - if (rb_eval(node->nd_end)) { + if (RTEST(rb_eval(self, node->nd_end))) { node->nd_state = 0; } result = TRUE; } break; - case NODE_BREAK: - JUMP_TAG(TAG_BREAK); - break; - - case NODE_CONTINUE: - JUMP_TAG(TAG_CONTINUE); - break; - - case NODE_REDO: - JUMP_TAG(TAG_REDO); - break; - - case NODE_RETRY: - JUMP_TAG(TAG_RETRY); - break; - case NODE_RETURN: - if (node->nd_stts) last_val = rb_eval(node->nd_stts); - JUMP_TAG(TAG_RETURN); + JUMP_TAG2(TAG_RETURN,(node->nd_stts)?rb_eval(self, node->nd_stts):Qnil); break; case NODE_CALL: @@ -1053,7 +1191,7 @@ rb_eval(node) int argc; VALUE *argv; /* used in SETUP_ARGS */ PUSH_ITER(ITER_NOT); - recv = rb_eval(node->nd_recv); + recv = rb_eval(self, node->nd_recv); SETUP_ARGS; POP_ITER(); result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0); @@ -1067,7 +1205,7 @@ rb_eval(node) PUSH_ITER(ITER_NOT); SETUP_ARGS; POP_ITER(); - result = rb_call(CLASS_OF(Qself),Qself,node->nd_mid,argc,argv,1); + result = rb_call(CLASS_OF(self),self,node->nd_mid,argc,argv,1); } break; @@ -1086,8 +1224,8 @@ rb_eval(node) POP_ITER(); } - PUSH_ITER(iter->iter?ITER_PRE:ITER_NOT); - result = rb_call(the_frame->last_class->super, Qself, + PUSH_ITER(the_iter->iter?ITER_PRE:ITER_NOT); + result = rb_call(the_frame->last_class->super, self, the_frame->last_func, argc, argv, 1); POP_ITER(); } @@ -1095,11 +1233,14 @@ rb_eval(node) case NODE_SCOPE: { + VALUE save = the_frame->cbase; + PUSH_SCOPE(); PUSH_TAG(); - if (node->nd_cnt > 0) { - the_scope->local_vars = ALLOCA_N(VALUE, node->nd_cnt); - MEMZERO(the_scope->local_vars, VALUE, node->nd_cnt); + if (node->nd_rval) the_frame->cbase = (VALUE)node->nd_rval; + if (node->nd_tbl) { + the_scope->local_vars = ALLOCA_N(VALUE, node->nd_tbl[0]); + memclear(the_scope->local_vars, node->nd_tbl[0]); the_scope->local_tbl = node->nd_tbl; } else { @@ -1107,11 +1248,12 @@ rb_eval(node) the_scope->local_tbl = 0; } if ((state = EXEC_TAG()) == 0) { - result = rb_eval(node->nd_body); + result = rb_eval(self, node->nd_body); } POP_TAG(); POP_SCOPE(); - if (state != 0) JUMP_TAG(state); + the_frame->cbase = save; + if (state) JUMP_TAG(state); } break; @@ -1120,12 +1262,12 @@ rb_eval(node) VALUE recv, args, val; NODE *rval; - recv = rb_eval(node->nd_recv); + recv = rb_eval(self, node->nd_recv); rval = node->nd_args->nd_head; - args = rb_eval(node->nd_args->nd_next); + args = rb_eval(self, node->nd_args->nd_next); val = rb_apply(recv, aref, args); - val = rb_funcall(val, node->nd_mid, 1, rb_eval(rval)); + val = rb_funcall(val, node->nd_mid, 1, rb_eval(self, rval)); ary_push(args, val); rb_apply(recv, aset, args); result = val; @@ -1134,38 +1276,40 @@ rb_eval(node) case NODE_OP_ASGN2: { - ID id = node->nd_aid; + ID id = node->nd_next->nd_vid; VALUE recv, val; - recv = rb_funcall(rb_eval(node->nd_recv), id, 0); + recv = rb_eval(self, node->nd_recv); + val = rb_funcall(recv, id, 0); - id = id_attrset(id); + val = rb_funcall(recv, node->nd_next->nd_mid, 2, val, + rb_eval(self, node->nd_value)); - val = rb_eval(node->nd_value); - rb_funcall(recv, id, 1, val); + rb_funcall2(recv, id_attrset(id), 1, &val); result = val; } break; case NODE_MASGN: - result = masign(node, rb_eval(node->nd_value)); + result = masign(self, node, rb_eval(self, node->nd_value)); break; case NODE_LASGN: if (the_scope->local_vars == 0) Bug("unexpected local variable asignment"); - result = the_scope->local_vars[node->nd_cnt] = rb_eval(node->nd_value); + the_scope->local_vars[node->nd_cnt] = rb_eval(self, node->nd_value); + result = the_scope->local_vars[node->nd_cnt]; break; case NODE_DASGN: - result = dyna_var_asgn(node->nd_vid, rb_eval(node->nd_value)); + result = dyna_var_asgn(node->nd_vid, rb_eval(self, node->nd_value)); break; case NODE_GASGN: { VALUE val; - val = rb_eval(node->nd_value); + val = rb_eval(self, node->nd_value); rb_gvar_set(node->nd_entry, val); result = val; } @@ -1175,8 +1319,8 @@ rb_eval(node) { VALUE val; - val = rb_eval(node->nd_value); - rb_ivar_set(Qself, node->nd_vid, val); + val = rb_eval(self, node->nd_value); + rb_ivar_set(self, node->nd_vid, val); result = val; } break; @@ -1185,15 +1329,21 @@ rb_eval(node) { VALUE val; - val = rb_eval(node->nd_value); + val = rb_eval(self, node->nd_value); + /* check for static scope constants */ + if (verbose && ev_const_defined(the_frame->cbase, node->nd_vid)) { + Warning("already initialized constnant %s", + rb_id2name(node->nd_vid)); + } rb_const_set(the_class, node->nd_vid, val); result = val; } break; case NODE_LVAR: - if (the_scope->local_vars == 0) + if (the_scope->local_vars == 0) { Bug("unexpected local variable"); + } result = the_scope->local_vars[node->nd_cnt]; break; @@ -1206,18 +1356,13 @@ rb_eval(node) break; case NODE_IVAR: - result = rb_ivar_get(Qself, node->nd_vid); + result = rb_ivar_get(self, node->nd_vid); break; case NODE_CVAR: - { - VALUE val; - - val = rb_const_get(node->nd_rval->nd_clss, node->nd_vid); - nd_set_type(node, NODE_CONST); - node->nd_cval = val; - result = val; - } + result = ev_const_get(the_frame->cbase, node->nd_vid); + nd_set_type(node, NODE_CONST); + node->nd_cval = result; break; case NODE_CONST: @@ -1228,7 +1373,7 @@ rb_eval(node) { VALUE cls; - cls = rb_eval(node->nd_head); + cls = rb_eval(self, node->nd_head); switch (TYPE(cls)) { case T_CLASS: case T_MODULE: @@ -1237,7 +1382,7 @@ rb_eval(node) Check_Type(cls, T_CLASS); break; } - result = rb_const_get(cls, node->nd_mid); + result = rb_const_get_at(cls, node->nd_mid); } break; @@ -1273,11 +1418,11 @@ rb_eval(node) list = node->nd_head; while (list) { - key = rb_eval(list->nd_head); + key = rb_eval(self, list->nd_head); list = list->nd_next; if (list == 0) Bug("odd number list for Hash"); - val = rb_eval(list->nd_head); + val = rb_eval(self, list->nd_head); list = list->nd_next; hash_aset(hash, key, val); } @@ -1297,7 +1442,7 @@ rb_eval(node) i = node->nd_alen; ary = ary_new2(i); for (i=0;node;node=node->nd_next) { - RARRAY(ary)->ptr[i++] = rb_eval(node->nd_head); + RARRAY(ary)->ptr[i++] = rb_eval(self, node->nd_head); RARRAY(ary)->len = i; } @@ -1309,9 +1454,10 @@ rb_eval(node) result = str_new3(node->nd_lit); break; - case NODE_STR2: - case NODE_XSTR2: + case NODE_DSTR: + case NODE_DXSTR: case NODE_DREGX: + case NODE_DREGX_ONCE: { VALUE str, str2; NODE *list = node->nd_next; @@ -1322,7 +1468,15 @@ rb_eval(node) str2 = list->nd_head->nd_lit; } else { - str2 = rb_eval(list->nd_head); + if (nd_type(list->nd_head) == NODE_EVSTR) { + rb_in_eval++; + list->nd_head = compile(list->nd_head->nd_lit); + rb_in_eval--; + if (!node) { + compile_error("string expand"); + } + } + str2 = rb_eval(self, list->nd_head); } if (str2) { str2 = obj_as_string(str2); @@ -1330,22 +1484,29 @@ rb_eval(node) } list = list->nd_next; } - if (nd_type(node) == NODE_DREGX) { - VALUE re = reg_new(RSTRING(str)->ptr, RSTRING(str)->len, - node->nd_cflag); - result = re; - } - else if (nd_type(node) == NODE_XSTR2) { - result = rb_xstring(str); - } - else { + switch (nd_type(node)) { + case NODE_DREGX: + result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len, + node->nd_cflag); + break; + case NODE_DREGX_ONCE: /* regexp expand once */ + result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len, + node->nd_cflag); + nd_set_type(node, NODE_LIT); + node->nd_lit = result; + break; + case NODE_DXSTR: + result = rb_funcall(self, '`', 1, str); + break; + default: result = str; + break; } } break; case NODE_XSTR: - result = rb_xstring(node->nd_lit); + result = rb_funcall(self, '`', 1, node->nd_lit); break; case NODE_LIT: @@ -1354,8 +1515,8 @@ rb_eval(node) case NODE_ATTRSET: if (the_frame->argc != 1) - Fail("Wrong # of arguments(%d for 1)", the_frame->argc); - result = rb_ivar_set(Qself, node->nd_vid, the_frame->argv[0]); + ArgError("Wrong # of arguments(%d for 1)", the_frame->argc); + result = rb_ivar_set(self, node->nd_vid, the_frame->argv[0]); break; case NODE_DEFN: @@ -1365,9 +1526,17 @@ rb_eval(node) int noex; body = search_method(the_class, node->nd_mid, &origin); - if (body && verbose && origin != (VALUE)the_class - && body->nd_noex != node->nd_noex) { - Warning("change method %s's scope", rb_id2name(node->nd_mid)); + if (body) { + if (origin == (VALUE)the_class) { + Warning("redefine %s", rb_id2name(node->nd_mid)); + } + else { + if (body->nd_noex != node->nd_noex) { + Warning("change method %s's scope", + rb_id2name(node->nd_mid)); + } + } + rb_clear_cache(); } if (body) noex = body->nd_noex; @@ -1380,23 +1549,38 @@ rb_eval(node) case NODE_DEFS: if (node->nd_defn) { - VALUE recv = rb_eval(node->nd_recv); + VALUE recv = rb_eval(self, node->nd_recv); + VALUE class; + NODE *body; + + if (NIL_P(recv)) { + TypeError("Can't define method \"%s\" for nil", + rb_id2name(node->nd_mid)); + } - if (recv == Qnil) { - Fail("Can't define method \"%s\" for nil", - rb_id2name(node->nd_mid)); + class = rb_singleton_class(recv); + if (st_lookup(RCLASS(class)->m_tbl, node->nd_mid, &body)) { + Warning("redefine %s", rb_id2name(node->nd_mid)); } + rb_clear_cache(); rb_funcall(recv, rb_intern("singleton_method_added"), 1, INT2FIX(node->nd_mid)); - rb_add_method(rb_singleton_class(recv),node->nd_mid,node->nd_defn, - NOEX_PUBLIC); + rb_add_method(class, node->nd_mid, node->nd_defn, NOEX_PUBLIC); result = Qnil; } break; case NODE_UNDEF: - rb_add_method(the_class, node->nd_mid, Qnil, NOEX_PUBLIC); - result = Qnil; + { + NODE *body; + + if (st_lookup(the_class->m_tbl, node->nd_mid, &body)) { + Warning("redefine %s", rb_id2name(node->nd_mid)); + } + rb_clear_cache(); + rb_add_method(the_class, node->nd_mid, 0, NOEX_PUBLIC); + result = Qnil; + } break; case NODE_ALIAS: @@ -1410,35 +1594,33 @@ rb_eval(node) struct RClass *tmp; if (node->nd_super) { - super = rb_eval(node->nd_super); - if (super == Qnil || TYPE(super) != T_CLASS) { - Fail("superclass undefined"); - } + super = superclass(self, node->nd_super); } else { - super = Qnil; + super = 0; } - if (rb_const_defined(the_class, node->nd_cname)) { + if (!rb_autoload_defined(node->nd_cname) && + ev_const_defined(the_frame->cbase, node->nd_cname)) { class = rb_const_get(the_class, node->nd_cname); + if (TYPE(class) != T_CLASS) + TypeError("%s is not a class", rb_id2name(node->nd_cname)); if (super) { - if (TYPE(class) != T_CLASS) - Fail("%s is not a class", rb_id2name(node->nd_cname)); tmp = RCLASS(class)->super; - while (FL_TEST(tmp, FL_SINGLE)) { + while (FL_TEST(tmp, FL_SINGLETON)) { tmp = RCLASS(tmp)->super; } while (TYPE(tmp) == T_ICLASS) { tmp = RCLASS(tmp)->super; } if (tmp != RCLASS(super)) - Fail("superclass mismatch for %s", - rb_id2name(node->nd_cname)); + TypeError("superclass mismatch for %s", + rb_id2name(node->nd_cname)); } Warning("extending class %s", rb_id2name(node->nd_cname)); } else { - if (super == Qnil) super = cObject; + if (!super) super = cObject; class = rb_define_class_id(node->nd_cname, super); rb_const_set(the_class, node->nd_cname, class); rb_set_class_path(class,the_class,rb_id2name(node->nd_cname)); @@ -1453,10 +1635,11 @@ rb_eval(node) { VALUE module; - if (rb_const_defined(the_class, node->nd_cname)) { + if (!rb_autoload_defined(node->nd_cname) && + ev_const_defined(the_frame->cbase, node->nd_cname)) { module = rb_const_get(the_class, node->nd_cname); if (TYPE(module) != T_MODULE) - Fail("%s is not a module", rb_id2name(node->nd_cname)); + TypeError("%s is not a module", rb_id2name(node->nd_cname)); Warning("extending module %s", rb_id2name(node->nd_cname)); } else { @@ -1473,8 +1656,11 @@ rb_eval(node) case NODE_DEFINED: { VALUE obj; + char buf[20]; + char *desc = 0; node = node->nd_head; + switch (nd_type(node)) { case NODE_SUPER: case NODE_ZSUPER: @@ -1486,17 +1672,16 @@ rb_eval(node) break; case NODE_FCALL: - obj = CLASS_OF(Qself); + obj = CLASS_OF(self); goto check_bound; case NODE_CALL: PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { - obj = rb_eval(node->nd_recv); + obj = rb_eval(self, node->nd_recv); } POP_TAG(); - if (state == TAG_FAIL) { - result = FALSE; + if (state) { break; } else { @@ -1505,27 +1690,23 @@ rb_eval(node) check_bound: if (method_boundp(obj, node->nd_mid, nd_type(node)== NODE_CALL)) { - result = TRUE; + desc = "method"; } - else result = FALSE; } break; case NODE_YIELD: - result = iterator_p(); + if (iterator_p()) { + desc = "iterator"; + } break; - case NODE_BREAK: - case NODE_CONTINUE: - case NODE_REDO: - case NODE_RETRY: - case NODE_SELF: + desc = "self"; break; case NODE_NIL: - case NODE_FAIL: - case NODE_ATTRSET: - case NODE_DEFINED: + desc = "nil"; break; + case NODE_ATTRSET: case NODE_OP_ASGN1: case NODE_OP_ASGN2: case NODE_MASGN: @@ -1534,60 +1715,80 @@ rb_eval(node) case NODE_GASGN: case NODE_IASGN: case NODE_CASGN: + desc = "asignment"; break; + case NODE_LVAR: + desc = "local-variable"; break; case NODE_DVAR: - result = TRUE; - break; + desc = "dynamic-local-variable"; break; case NODE_GVAR: - result = rb_gvar_defined(node->nd_entry); + if (rb_gvar_defined(node->nd_entry)) { + desc = "global-variable"; + } break; case NODE_IVAR: - result = rb_ivar_defined(node->nd_vid); + if (rb_ivar_defined(self, node->nd_vid)) { + desc = "instance-variable"; + } break; case NODE_CVAR: - result = rb_const_defined(node->nd_rval->nd_clss, node->nd_vid); - break; - - case NODE_CONST: - result = TRUE; + if (ev_const_defined(the_frame->cbase, node->nd_vid)) { + case NODE_CONST: /* jump in */ + desc = "class-constant"; + } break; case NODE_COLON2: PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { - obj = rb_eval(node->nd_head); + obj = rb_eval(self, node->nd_head); } POP_TAG(); - if (state == TAG_FAIL) result = FALSE; + if (state) { + break; + } else { if (state) JUMP_TAG(state); - result = rb_const_defined(obj, node->nd_mid); + switch (TYPE(obj)) { + case T_CLASS: + case T_MODULE: + if (rb_const_defined_at(obj, node->nd_mid)) + desc = "class-constant"; + break; + } } break; case NODE_NTH_REF: - result = reg_nth_defined(node->nd_nth, MATCH_DATA); + if (reg_nth_defined(node->nd_nth, MATCH_DATA)) { + sprintf(buf, "$%d", node->nd_nth); + desc = buf; + } break; case NODE_BACK_REF: - result = reg_nth_defined(0, MATCH_DATA); + if (reg_nth_defined(0, MATCH_DATA)) { + sprintf(buf, "$%c", node->nd_nth); + desc = buf; + } break; default: PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { - rb_eval(node); + rb_eval(self, node); } POP_TAG(); - if (state == TAG_FAIL) result = FALSE; + if (state) break; else { - if (state) JUMP_TAG(state); - result = TRUE; + desc = "expression"; } } + if (desc) result = str_new2(desc); + else result = FALSE; } break; @@ -1595,12 +1796,8 @@ rb_eval(node) Bug("unknown node type %d", nd_type(node)); } finish: -#ifdef SAFE_SIGHANDLE - if (trap_pending) { - rb_trap_exec(); - } -#endif - return result; /* not reached */ + CHECK_INTS; + return result; } static void @@ -1608,7 +1805,8 @@ module_setup(module, node) VALUE module; NODE *node; { - int state; + NODE *state; + VALUE save = the_frame->cbase; /* fill c-ref */ node->nd_clss = module; @@ -1616,12 +1814,12 @@ module_setup(module, node) PUSH_CLASS(); the_class = (struct RClass*)module; - PUSH_SELF((VALUE)the_class); PUSH_SCOPE(); - if (node->nd_cnt > 0) { - the_scope->local_vars = ALLOCA_N(VALUE, node->nd_cnt); - MEMZERO(the_scope->local_vars, VALUE, node->nd_cnt); + if (node->nd_rval) the_frame->cbase = node->nd_rval; + if (node->nd_tbl) { + the_scope->local_vars = ALLOCA_N(VALUE, node->nd_tbl[0]); + memclear(the_scope->local_vars, node->nd_tbl[0]); the_scope->local_tbl = node->nd_tbl; } else { @@ -1631,21 +1829,48 @@ module_setup(module, node) PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { - rb_eval(node->nd_body); + rb_eval((VALUE)the_class, node->nd_body); } POP_TAG(); POP_SCOPE(); - POP_SELF(); POP_CLASS(); + the_frame->cbase = save; if (state) JUMP_TAG(state); } -VALUE -rb_responds_to(obj, id) +int +rb_respond_to(obj, id) VALUE obj; ID id; { - if (rb_method_boundp(CLASS_OF(obj), id)) { + if (rb_method_boundp(CLASS_OF(obj), id, 0)) { + return TRUE; + } + return FALSE; +} + +static VALUE +krn_respond_to(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE mid, priv; + ID id; + + rb_scan_args(argc, argv, "11", &mid, &priv); + id = rb_to_id(mid); + if (rb_method_boundp(CLASS_OF(obj), id, !RTEST(priv))) { + return TRUE; + } + return FALSE; +} + +static VALUE +mod_method_defined(mod, mid) + VALUE mod, mid; +{ + if (rb_method_boundp(mod, rb_to_id(mid), TRUE)) { return TRUE; } return FALSE; @@ -1655,11 +1880,11 @@ void rb_exit(status) int status; { - last_val = INT2FIX(status); - if (prot_tag) - JUMP_TAG(TAG_EXIT); - rb_trap_exit(); - exit(FIX2UINT(last_val)); + if (prot_tag) { + exit_status = status; + rb_raise(exc_new(eSystemExit, "")); + } + exit(status); } static VALUE @@ -1671,71 +1896,141 @@ f_exit(argc, argv, obj) VALUE status; if (rb_scan_args(argc, argv, "01", &status) == 1) { - Need_Fixnum(status); + status = NUM2INT(status); } else { - status = INT2FIX(0); + status = 0; } - last_val = status; - JUMP_TAG(TAG_EXIT); - - return Qnil; /* not reached */ + rb_exit(status); + /* not reached */ } void rb_break() { - JUMP_TAG(TAG_BREAK); + JUMP_TAG2(TAG_BREAK, 0); } -void -rb_redo() +static VALUE +f_break() +{ + JUMP_TAG2(TAG_BREAK, 0); +} + +static VALUE +f_next() +{ + JUMP_TAG2(TAG_NEXT, 0); +} + +static VALUE +f_redo() +{ + JUMP_TAG2(TAG_REDO, 0); +} + +static VALUE +f_retry() +{ + JUMP_TAG2(TAG_RETRY, 0); +} + +#ifdef __GNUC__ +static volatile voidfn rb_longjmp; +#endif + +static VALUE make_backtrace(); + +static void +rb_longjmp(tag, mesg) + int tag; + VALUE mesg; { - JUMP_TAG(TAG_REDO); + if (NIL_P(errat) && NIL_P(mesg)) { + errinfo = exc_new(eRuntimeError, ""); + } + + if (sourcefile && (NIL_P(errat) || !NIL_P(mesg))) { + errat = make_backtrace(); + } + + if (!NIL_P(mesg)) { + if (obj_is_kind_of(mesg, eGlobalExit)) { + errinfo = mesg; + } + else { + errinfo = exc_new2(eRuntimeError, mesg); + } + str_freeze(errinfo); + } + + JUMP_TAG2(tag, 0); } void -rb_retry() +rb_raise(mesg) + VALUE mesg; { - JUMP_TAG(TAG_RETRY); + rb_longjmp(TAG_RAISE, mesg); } void -rb_fail(mesg) +rb_fatal(mesg) VALUE mesg; { - char buf[BUFSIZ]; + rb_longjmp(TAG_FATAL, mesg); +} - if (errat == Qnil && mesg == Qnil) { - errstr = Qnil; +void +rb_interrupt() +{ + rb_raise(exc_new(eInterrupt, "Interrupt")); +} + +static VALUE +f_raise(argc, argv) + int argc; + VALUE *argv; +{ + VALUE arg1, arg2; + VALUE etype, mesg; + + etype = eRuntimeError; + mesg = Qnil; + switch (rb_scan_args(argc, argv, "02", &arg1, &arg2)) { + case 1: + mesg = arg1; + break; + case 2: + etype = arg1; + mesg = arg2; + break; } - if (errat == Qnil && sourcefile) { - if (the_frame->last_func) { - last_func = the_frame->last_func; + if (!NIL_P(mesg)) { + Check_Type(mesg, T_STRING); + if (!obj_is_kind_of(mesg, eException)) { + mesg = exc_new2(etype, mesg); } - sprintf(buf, "%s:%d", sourcefile, sourceline); - errat = str_new2(buf); } - if (mesg) { - errstr = mesg; - } - if (prot_tag->level == 0) error_print(last_func); - JUMP_TAG(TAG_FAIL); + PUSH_FRAME(); /* fake frame */ + *the_frame = *_frame.prev->prev; + rb_raise(mesg); + POP_FRAME(); } -VALUE +int iterator_p() { - if (iter->iter) return TRUE; + if (the_frame->iter) return TRUE; return FALSE; } static VALUE f_iterator_p() { - if (iter->prev && iter->prev->iter) return TRUE; + if (the_frame->prev && the_frame->prev->iter) return TRUE; return FALSE; } @@ -1743,18 +2038,19 @@ VALUE rb_yield_0(val, self) VALUE val, self; { - struct BLOCK *block; NODE *node; - int state; + NODE *state; VALUE result = Qnil; + struct BLOCK *block; struct SCOPE *old_scope; struct FRAME frame; if (!iterator_p()) { - Fail("yield called out of iterator"); + Raise(eLocalJumpError, "yield called out of iterator"); } PUSH_VARS(); + PUSH_CLASS(); block = the_block; frame = block->frame; frame.prev = the_frame; @@ -1763,52 +2059,53 @@ rb_yield_0(val, self) the_scope = block->scope; the_block = block->prev; the_dyna_vars = block->d_vars; + the_class = block->class; + if (!self) self = block->self; + node = block->body; if (block->var) { if (nd_type(block->var) == NODE_MASGN) - masign(block->var, val); + masign(self, block->var, val); else - asign(block->var, val); + asign(self, block->var, val); } - node = block->body; - PUSH_ITER(block->iter); - PUSH_SELF(self?self:block->self); PUSH_TAG(); - switch (state = EXEC_TAG()) { + if ((state = EXEC_TAG()) == 0) { redo: - case 0: if (!node) { result = Qnil; } else if (nd_type(node) == NODE_CFUNC) { - result = (*node->nd_cfnc)(val,node->nd_argc); + result = (*node->nd_cfnc)(val,node->nd_argc,self); } else { - result = rb_eval(node); + result = rb_eval(self, node); + } + } + else { + switch (state->nd_tag) { + case TAG_REDO: + goto redo; + case TAG_NEXT: + state = 0; + break; + case TAG_BREAK: + case TAG_RETURN: + state->nd_tlev = block->level; + state->nd_tag = IN_BLOCK|state->nd_tag; + break; + default: + break; } - break; - case TAG_REDO: - goto redo; - case TAG_CONTINUE: - state = 0; - break; - case TAG_BREAK: - case TAG_RETURN: - target_level = block->level; - state = IN_BLOCK|state; - break; - default: - break; } POP_TAG(); - POP_SELF(); POP_ITER(); + POP_CLASS(); POP_VARS(); the_block = block; the_frame = the_frame->prev; the_scope = old_scope; if (state) JUMP_TAG(state); - return result; } @@ -1823,11 +2120,10 @@ static VALUE f_loop() { for (;;) { rb_yield(Qnil); } - return Qnil; } static VALUE -masign(node, val) +masign(self, node, val) NODE *node; VALUE val; { @@ -1842,30 +2138,31 @@ masign(node, val) } len = RARRAY(val)->len; for (i=0; list && i<len; i++) { - asign(list->nd_head, RARRAY(val)->ptr[i]); + asign(self, list->nd_head, RARRAY(val)->ptr[i]); list = list->nd_next; } if (node->nd_args) { if (!list && i<len) { - asign(node->nd_args, ary_new4(len-i, RARRAY(val)->ptr+i)); + asign(self, node->nd_args, ary_new4(len-i, RARRAY(val)->ptr+i)); } else { - asign(node->nd_args, Qnil); + asign(self, node->nd_args, ary_new2(0)); } } } else if (node->nd_args) { - asign(node->nd_args, Qnil); + asign(self, node->nd_args, Qnil); } while (list) { - asign(list->nd_head, Qnil); + asign(self, list->nd_head, Qnil); list = list->nd_next; } return val; } static void -asign(lhs, val) +asign(self, lhs, val) + VALUE self; NODE *lhs; VALUE val; { @@ -1875,7 +2172,7 @@ asign(lhs, val) break; case NODE_IASGN: - rb_ivar_set(Qself, lhs->nd_vid, val); + rb_ivar_set(self, lhs->nd_vid, val); break; case NODE_LASGN: @@ -1895,16 +2192,16 @@ asign(lhs, val) case NODE_CALL: { VALUE recv; - recv = rb_eval(lhs->nd_recv); - if (lhs->nd_args->nd_head == Qnil) { + recv = rb_eval(self, lhs->nd_recv); + if (!lhs->nd_args->nd_head) { /* attr set */ - rb_funcall(recv, lhs->nd_mid, 1, val); + rb_funcall2(recv, lhs->nd_mid, 1, &val); } else { /* array set */ VALUE args; - args = rb_eval(lhs->nd_args); + args = rb_eval(self, lhs->nd_args); RARRAY(args)->ptr[RARRAY(args)->len-1] = val; rb_apply(recv, lhs->nd_mid, args); } @@ -1922,13 +2219,15 @@ rb_iterate(it_proc, data1, bl_proc, data2) VALUE (*it_proc)(), (*bl_proc)(); void *data1, *data2; { - int state; + NODE *state; VALUE retval = Qnil; NODE *node = NEW_CFUNC(bl_proc, data2); + VALUE self = TopSelf; + int tag_level; iter_retry: PUSH_ITER(ITER_PRE); - PUSH_BLOCK(Qnil, node); + PUSH_BLOCK(0, node); PUSH_TAG(); state = EXEC_TAG(); @@ -1937,33 +2236,53 @@ rb_iterate(it_proc, data1, bl_proc, data2) } POP_TAG(); + tag_level = the_block->level; POP_BLOCK(); POP_ITER(); - switch (state) { - case 0: - break; + if (state) { + switch (state->nd_tag) { + case TAG_RETRY: + goto iter_retry; - case TAG_RETRY: - goto iter_retry; + case IN_BLOCK|TAG_BREAK: + if (state->nd_tlev != tag_level) { + JUMP_TAG(state); + } + retval = Qnil; + break; - case IN_BLOCK|TAG_BREAK: - if (target_level != tag_level) { + case IN_BLOCK|TAG_RETURN: + if (state->nd_tlev == tag_level) { + state->nd_tag &= ~IN_BLOCK; + } + /* fall through */ + default: JUMP_TAG(state); } - retval = Qnil; - break; + } + return retval; +} - case IN_BLOCK|TAG_RETURN: - if (target_level == tag_level) { - state &= ~IN_BLOCK; - } - /* fall through */ - default: - JUMP_TAG(state); +static int +handle_rescue(self, node) + VALUE self; + NODE *node; +{ + int argc; VALUE *argv; /* used in SETUP_ARGS */ + + if (!node->nd_args) { + return obj_is_kind_of(errinfo, eException); } - return retval; + PUSH_ITER(ITER_NOT); + SETUP_ARGS; + POP_ITER(); + while (argc--) { + if (obj_is_kind_of(errinfo, argv[0])) return 1; + argv++; + } + return 0; } VALUE @@ -1971,43 +2290,36 @@ rb_rescue(b_proc, data1, r_proc, data2) VALUE (*b_proc)(), (*r_proc)(); void *data1, *data2; { - int state; - VALUE result = Qnil; - volatile SIGHANDLE handle; + NODE *state; + VALUE result; PUSH_TAG(); - switch (state = EXEC_TAG()) { - case 0: - handle = sig_beg(); + if ((state = EXEC_TAG()) == 0) { retry_entry: result = (*b_proc)(data1); - break; - - case TAG_FAIL: - sig_end(handle); - if (r_proc) { - PUSH_TAG(); - state = EXEC_TAG(); - if (state == 0) { - result = (*r_proc)(data2); + } + else { + if (state->nd_tag == TAG_RAISE) { + if (r_proc) { + PUSH_TAG(); + state = EXEC_TAG(); + if (state == 0) { + result = (*r_proc)(data2, errinfo); + } + POP_TAG(); + if (state && state->nd_tag == TAG_RETRY) { + state = 0; + goto retry_entry; + } } - POP_TAG(); - if (state == TAG_RETRY) { - goto retry_entry; + else { + result = Qnil; + state = 0; + } + if (state == 0) { + errat = Qnil; } } - else { - state = 0; - } - if (state == 0) { - errat = Qnil; - last_func = 0; - } - break; - - default: - sig_end(handle); - break; } POP_TAG(); if (state) JUMP_TAG(state); @@ -2020,7 +2332,7 @@ rb_ensure(b_proc, data1, e_proc, data2) VALUE (*b_proc)(), (*e_proc)(); void *data1, *data2; { - int state; + NODE *state; VALUE result = Qnil; PUSH_TAG(); @@ -2030,7 +2342,7 @@ rb_ensure(b_proc, data1, e_proc, data2) POP_TAG(); (*e_proc)(data2); - if (state != 0) { + if (state) { JUMP_TAG(state); } return result; @@ -2044,7 +2356,7 @@ f_missing(argc, argv, obj) VALUE *argv; VALUE obj; { - VALUE desc; + VALUE desc = 0; ID id; char *format; struct FRAME *frame; @@ -2052,27 +2364,44 @@ f_missing(argc, argv, obj) id = FIX2INT(argv[0]); argc--; argv++; - if (TYPE(obj) == T_STRING) { - desc = krn_inspect(obj); - } - else { + switch (TYPE(obj)) { + case T_NIL: + format = "undefined method `%s' for nil"; + break; + case T_TRUE: + format = "undefined method `%s' for TRUE"; + break; + case T_FALSE: + format = "undefined method `%s' for FALSE"; + break; + case T_OBJECT: desc = obj_as_string(obj); + break; + default: + desc = rb_inspect(obj); + break; + } + if (desc) { + if (last_noex) + format = "private method `%s' called for %s(%s)"; + else if (argc == 0) { + format = "undefined local variable or method `%s' for %s(%s)"; + } + else { + format = "undefined method `%s' for %s(%s)"; + } + if (RSTRING(desc)->len > 65) { + desc = krn_to_s(obj); + } } - if (last_noex) - format = "method `%s' not available for %s(%s)"; - else - format = "undefined method `%s' for %s(%s)"; - /* fake frame */ - PUSH_FRAME(); - frame = the_frame->prev; - *the_frame = *frame->prev; - the_frame->prev = frame; - - Fail(format, - rb_id2name(id), - RSTRING(desc)->ptr, - rb_class2name(CLASS_OF(obj))); + PUSH_FRAME(); /* fake frame */ + *the_frame = *_frame.prev->prev; + + NameError(format, + rb_id2name(id), + desc?RSTRING(desc)->ptr:"", + desc?rb_class2name(CLASS_OF(obj)):""); POP_FRAME(); } @@ -2095,8 +2424,20 @@ rb_undefined(obj, id, argc, argv, noex) return rb_funcall2(obj, rb_intern("method_missing"), argc+1, nargv); } -#define STACK_LEVEL_MAX 10000 -static int stack_level; +#define STACK_LEVEL_MAX 655350 +extern VALUE *gc_stack_start; +static int +stack_length() +{ + VALUE pos; + +#ifdef sparc + return gc_stack_start - &pos + 0x80; +#else + return (&pos < gc_stack_start) ? gc_stack_start - &pos + : &pos - gc_stack_start; +#endif +} static VALUE rb_call(class, recv, mid, argc, argv, scope) @@ -2109,38 +2450,30 @@ rb_call(class, recv, mid, argc, argv, scope) { NODE *body; int noex; - VALUE result = Qnil; + ID id = mid; struct cache_entry *ent; + VALUE result = Qnil; int itr; enum node_type type; + static int tick; /* is it in the method cache? */ ent = cache + EXPR1(class, mid); if (ent->mid == mid && ent->class == class) { class = ent->origin; - mid = ent->mid; - body = ent->method; + id = ent->mid0; noex = ent->noex; + body = ent->method; } - else { - ID id = mid; - - if ((body = rb_get_method_body(&class, &id, &noex)) == FALSE) { - return rb_undefined(recv, mid, argc, argv, 0); - } - mid = id; + else if ((body = rb_get_method_body(&class, &id, &noex)) == 0) { + return rb_undefined(recv, mid, argc, argv, 0); } - switch (noex) { - case NOEX_PUBLIC: - break; - case NOEX_PRIVATE: - if (scope == 0) /* receiver specified */ - return rb_undefined(recv, mid, argc, argv, 1); - break; - } + /* receiver specified form for private method */ + if (noex == NOEX_PRIVATE && scope == 0) + return rb_undefined(recv, mid, argc, argv, 1); - switch (iter->iter) { + switch (the_iter->iter) { case ITER_PRE: itr = ITER_CUR; break; @@ -2153,16 +2486,15 @@ rb_call(class, recv, mid, argc, argv, scope) type = nd_type(body); if (type == NODE_ZSUPER) { /* for re-scoped method */ - return rb_call(class->super, recv, mid, argc, argv, scope?scope:1); + return rb_call(class->super, recv, id, argc, argv, scope?scope:1); } - if (stack_level++ > STACK_LEVEL_MAX) - Fail("stack level too deep"); + if (++tick % 1000 == 0 && stack_length() > STACK_LEVEL_MAX) + Fatal("stack level too deep"); PUSH_ITER(itr); - PUSH_SELF(recv); PUSH_FRAME(); - the_frame->last_func = mid; + the_frame->last_func = id; the_frame->last_class = class; the_frame->argc = argc; the_frame->argv = argv; @@ -2173,7 +2505,7 @@ rb_call(class, recv, mid, argc, argv, scope) int len = body->nd_argc; if (len >= 0 && argc != len) { - Fail("Wrong # of arguments(%d for %d)", argc, len); + ArgError("Wrong # of arguments(%d for %d)", argc, len); } switch (len) { @@ -2273,7 +2605,7 @@ rb_call(class, recv, mid, argc, argv, scope) len, rb_class2name(class), rb_id2name(mid)); } else { - Fail("too many arguments(%d)", len); + ArgError("too many arguments(%d)", len); } break; } @@ -2283,19 +2615,20 @@ rb_call(class, recv, mid, argc, argv, scope) /* for attr get/set */ case NODE_ATTRSET: case NODE_IVAR: - result = rb_eval(body); + result = rb_eval(recv, body); break; default: { - int state; + NODE *state; VALUE *local_vars; PUSH_SCOPE(); - if (body->nd_cnt > 0) { - local_vars = ALLOCA_N(VALUE, body->nd_cnt); - MEMZERO(local_vars, VALUE, body->nd_cnt); + if (body->nd_rval) the_frame->cbase = body->nd_rval; + if (body->nd_tbl) { + local_vars = ALLOCA_N(VALUE, body->nd_tbl[0]); + memclear(local_vars, body->nd_tbl[0]); the_scope->local_tbl = body->nd_tbl; the_scope->local_vars = local_vars; } @@ -2306,6 +2639,8 @@ rb_call(class, recv, mid, argc, argv, scope) body = body->nd_body; PUSH_TAG(); + PUSH_VARS(); + dyna_var_mark(); state = EXEC_TAG(); if (state == 0) { if (nd_type(body) == NODE_BLOCK) { @@ -2321,7 +2656,7 @@ rb_call(class, recv, mid, argc, argv, scope) if (i > argc || (node->nd_rest == -1 && i+(node->nd_opt?node->nd_opt->nd_alen:0)<argc)){ - Fail("Wrong # of arguments(%d for %d)", argc, i); + ArgError("Wrong # of arguments(%d for %d)", argc, i); } if (local_vars) { @@ -2333,56 +2668,54 @@ rb_call(class, recv, mid, argc, argv, scope) NODE *opt = node->nd_opt; while (opt && argc) { - asign(opt->nd_head, *argv); + asign(recv, opt->nd_head, *argv); argv++; argc--; opt = opt->nd_next; } - rb_eval(opt); + rb_eval(recv, opt); } if (node->nd_rest >= 0) { if (argc > 0) local_vars[node->nd_rest]=ary_new4(argc,argv); else - local_vars[node->nd_rest] = ary_new2(0); + local_vars[node->nd_rest]=ary_new2(0); } } } else if (nd_type(body) == NODE_ARGS) { body = 0; } - result = rb_eval(body); + result = rb_eval(recv, body); } + POP_VARS(); POP_TAG(); POP_SCOPE(); - switch (state) { - case 0: - break; - case TAG_CONTINUE: - Fatal("unexpected continue"); - break; - case TAG_BREAK: - Fatal("unexpected break"); - break; - case TAG_REDO: - Fatal("unexpected redo"); - break; - case TAG_RETURN: - result = last_val; - break; - case TAG_RETRY: - if (!iterator_p()) { - Fatal("retry outside of rescue clause"); + if (state) { + switch (state->nd_tag) { + case TAG_NEXT: + Raise(eLocalJumpError, "unexpected next"); + break; + case TAG_BREAK: + Raise(eLocalJumpError, "unexpected break"); + break; + case TAG_REDO: + Raise(eLocalJumpError, "unexpected redo"); + break; + case TAG_RETURN: + result = state->nd_tval; + break; + case TAG_RETRY: + if (!iterator_p()) { + Raise(eLocalJumpError, "retry outside of rescue clause"); + } + default: + JUMP_TAG(state); } - default: - stack_level--; - JUMP_TAG(state); } } } POP_FRAME(); - POP_SELF(); POP_ITER(); - stack_level--; return result; } @@ -2410,7 +2743,7 @@ f_send(argc, argv, recv) VALUE vid; ID mid; - if (argc == 0) Fail("no method name given"); + if (argc == 0) ArgError("no method name given"); vid = argv[0]; argc--; argv++; if (TYPE(vid) == T_STRING) { @@ -2463,26 +2796,31 @@ rb_funcall2(recv, mid, argc, argv) } static VALUE -f_caller(argc, argv) - int argc; - VALUE *argv; +backtrace(lev) + int lev; { - VALUE level; struct FRAME *frame = the_frame; - int lev, n; char buf[BUFSIZ]; + VALUE ary; - rb_scan_args(argc, argv, "01", &level); - if (level == Qnil) lev = 1; - else lev = NUM2INT(level); - n = lev; - if (n < 0) Fail("negative level(%d)", n); + ary = ary_new(); + if (lev < 0) { + if (frame->last_func) { + sprintf(buf, "%s:%d:in `%s'", sourcefile, sourceline, + rb_id2name(frame->last_func)); + } + else { + sprintf(buf, "%s:%d", sourcefile, sourceline); + } + ary_push(ary, str_new2(buf)); + } else { - while (n-- > 0) { + while (lev-- > 0) { frame = frame->prev; if (!frame) return Qnil; } - if (!frame->file) return Qnil; + } + while (frame && frame->file) { if (frame->prev && frame->prev->last_func) { sprintf(buf, "%s:%d:in `%s'", frame->file, frame->line, @@ -2491,73 +2829,169 @@ f_caller(argc, argv) else { sprintf(buf, "%s:%d", frame->file, frame->line); } + ary_push(ary, str_new2(buf)); + frame = frame->prev; } - return str_new2(buf); + return ary; +} + +static VALUE +f_caller(argc, argv) + int argc; + VALUE *argv; +{ + VALUE level; + struct FRAME *frame = the_frame; + int lev; + + rb_scan_args(argc, argv, "01", &level); + + if (NIL_P(level)) lev = 1; + else lev = NUM2INT(level); + if (lev < 0) ArgError("negative level(%d)", lev); + + return backtrace(lev); } void rb_backtrace() { - VALUE c, lev; - int n = 0; + int i, lev; + VALUE ary, c; - lev = INT2FIX(n); - while (c = f_caller(1, &lev)) { - printf("%s\n", RSTRING(c)->ptr); - n++; - lev = INT2FIX(n); + lev = INT2FIX(0); + ary = backtrace(-1); + for (i=0; i<RARRAY(ary)->len; i++) { + printf("\tfrom %s\n", RSTRING(RARRAY(ary)->ptr)->ptr); } } +static VALUE +make_backtrace() +{ + VALUE lev; + + lev = INT2FIX(0); + return backtrace(-1); +} + ID rb_frame_last_func() { return the_frame->last_func; } -int rb_in_eval = 0; +static NODE* +compile(src) + struct RString *src; +{ + NODE *node; + + Check_Type(src, T_STRING); + + errinfo = Qnil; + node = compile_string(sourcefile, src->ptr, src->len); + + if (nerrs == 0) return node; + return 0; +} + +static void blk_free(); static VALUE -f_eval(obj, src) - VALUE obj; +eval(self, src, scope) + VALUE self; struct RString *src; + struct RData *scope; { + struct BLOCK *data; VALUE result = Qnil; - int state; NODE *node; + NODE *state; + struct BLOCK *old_block; + struct SCOPE *old_scope; + struct FRAME frame; + char *file = sourcefile; + int line = sourceline; - Check_Type(src, T_STRING); PUSH_TAG(); - rb_in_eval = 1; - node = eval_tree; - PUSH_CLASS(); + if (!NIL_P(scope)) { + if (TYPE(scope) != T_DATA || scope->dfree != blk_free) { + TypeError("wrong argument type %s (expected Proc/Binding)", + rb_class2name(CLASS_OF(scope))); + } + + Get_Data_Struct(scope, struct BLOCK, data); + + /* PUSH BLOCK from data */ + frame = data->frame; + frame.prev = the_frame; + the_frame = &(frame); + old_scope = the_scope; + the_scope = data->scope; + old_block = the_block; + the_block = data->prev; + the_dyna_vars = data->d_vars; + the_class = data->class; + self = data->self; + } + + rb_in_eval++; if (TYPE(the_class) == T_ICLASS) { the_class = (struct RClass*)RBASIC(the_class)->class; } - if ((state = EXEC_TAG()) == 0) { - lex_setsrc("(eval)", src->ptr, src->len); - eval_tree = 0; - PUSH_VARS(); - yyparse(); - POP_VARS(); - if (nerrs == 0) { - result = Eval(); + if (!compile(src)) { + rb_in_eval--; + compile_error("eval()"); } + result = eval_node(self); + } + if (!NIL_P(scope)) { + the_frame = the_frame->prev; + the_scope = old_scope; + the_block = old_block; } - eval_tree = node; POP_CLASS(); POP_TAG(); - if (state) JUMP_TAG(state); - - if (nerrs > 0) { - syntax_error(); + rb_in_eval--; + if (state) { + VALUE err ; + + switch (state->nd_tag) { + case TAG_RAISE: + sourcefile = file; + sourceline = line; + if (strcmp(sourcefile, "(eval)") == 0) { + err = errat; + if (sourceline != 1) { + str_cat(err, ": ", 2); + str_cat(err, RSTRING(errinfo)->ptr, RSTRING(errinfo)->len); + } + errat = Qnil; + rb_raise(exc_new2(CLASS_OF(errinfo), err)); + } + rb_raise(Qnil); + } + JUMP_TAG(state); } return result; } +static VALUE +f_eval(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE src, scope; + + rb_scan_args(argc, argv, "11", &src, &scope); + return eval(self, src, scope); +} + VALUE rb_load_path; char *dln_find_file(); @@ -2567,20 +3001,18 @@ find_file(file) char *file; { extern VALUE rb_load_path; - VALUE sep, vpath; + VALUE vpath; char *path; if (file[0] == '/') return file; if (rb_load_path) { Check_Type(rb_load_path, T_ARRAY); - sep = str_new2(":"); - vpath = ary_join(rb_load_path, sep); + vpath = ary_join(rb_load_path, str_new2(":")); path = RSTRING(vpath)->ptr; - sep = Qnil; } else { - path = Qnil; + path = 0; } return dln_find_file(file, path); @@ -2591,36 +3023,39 @@ f_load(obj, fname) VALUE obj; struct RString *fname; { - int state, in_eval = rb_in_eval; + NODE *state; char *file, *src; + volatile ID last_func; Check_Type(fname, T_STRING); file = find_file(fname->ptr); - if (!file) Fail("No such file to load -- %s", fname->ptr); + if (!file) LoadError("No such file to load -- %s", fname->ptr); - PUSH_SELF(TopSelf); PUSH_TAG(); PUSH_CLASS(); the_class = (struct RClass*)cObject; PUSH_SCOPE(); the_scope->local_vars = top_scope->local_vars; the_scope->local_tbl = top_scope->local_tbl; - rb_in_eval = 1; + state = EXEC_TAG(); + last_func = the_frame->last_func; + the_frame->last_func = 0; if (state == 0) { + rb_in_eval++; rb_load_file(file); + rb_in_eval--; if (nerrs == 0) { - Eval(); + eval_node(TopSelf); } } + the_frame->last_func = last_func; top_scope->flag = the_scope->flag; POP_SCOPE(); POP_CLASS(); POP_TAG(); - POP_SELF(); - rb_in_eval = in_eval; if (nerrs > 0) { - rb_fail(errstr); + rb_raise(errinfo); } if (state) JUMP_TAG(state); @@ -2629,16 +3064,17 @@ f_load(obj, fname) static VALUE rb_features; -static VALUE +static int rb_provided(feature) char *feature; { + struct RArray *features = RARRAY(rb_features); VALUE *p, *pend; char *f; int len; - p = RARRAY(rb_features)->ptr; - pend = p + RARRAY(rb_features)->len; + p = features->ptr; + pend = p + features->len; while (p < pend) { Check_Type(*p, T_STRING); f = RSTRING(*p)->ptr; @@ -2653,6 +3089,11 @@ rb_provided(feature) return FALSE; } +#ifdef THREAD +static int thread_loading(); +static void thread_loading_done(); +#endif + void rb_provide(feature) char *feature; @@ -2670,7 +3111,8 @@ f_require(obj, fname) VALUE load; Check_Type(fname, T_STRING); - if (rb_provided(fname->ptr)) return FALSE; + if (rb_provided(fname->ptr)) + return FALSE; ext = strrchr(fname->ptr, '.'); if (ext) { @@ -2680,9 +3122,9 @@ f_require(obj, fname) if (file) goto rb_load; } else if (strcmp(".o", ext) == 0) { - feature = fname->ptr; + file = feature = fname->ptr; if (strcmp(".o", DLEXT) != 0) { - buf = ALLOCA_N(char, strlen(fname->ptr) + 3); + buf = ALLOCA_N(char, strlen(fname->ptr)+sizeof(DLEXT)+1); strcpy(buf, feature); ext = strrchr(buf, '.'); strcpy(ext, DLEXT); @@ -2696,7 +3138,7 @@ f_require(obj, fname) if (file) goto dyna_load; } } - buf = ALLOCA_N(char, strlen(fname->ptr) + 4); + buf = ALLOCA_N(char, strlen(fname->ptr) + 5); sprintf(buf, "%s.rb", fname->ptr); file = find_file(buf); if (file) { @@ -2710,58 +3152,80 @@ f_require(obj, fname) feature = buf; goto dyna_load; } - Fail("No such file to load -- %s", fname->ptr); + LoadError("No such file to load -- %s", fname->ptr); dyna_load: - load = str_new2(file); - file = RSTRING(load)->ptr; - dln_load(file); - rb_provide(feature); +#ifdef THREAD + if (thread_loading(feature)) return FALSE; + else { + NODE *state; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { +#endif + load = str_new2(file); + file = RSTRING(load)->ptr; + dln_load(file); + rb_provide(feature); +#ifdef THREAD + } + POP_TAG(); + thread_loading_done(); + if (state) JUMP_TAG(state); + } +#endif return TRUE; rb_load: - f_load(obj, fname); - rb_provide(feature); +#ifdef THREAD + if (thread_loading(feature)) return FALSE; + else { + NODE *state; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { +#endif + f_load(obj, fname); + rb_provide(feature); +#ifdef THREAD + } + POP_TAG(); + thread_loading_done(); + if (state) JUMP_TAG(state); + } +#endif return TRUE; } static void -set_method_visibility(argc, argv, ex) +set_method_visibility(self, argc, argv, ex) + VALUE self; int argc; VALUE *argv; int ex; { - VALUE self = Qself; int i; - ID id; for (i=0; i<argc; i++) { - if (FIXNUM_P(argv[i])) { - id = FIX2INT(argv[i]); - } - else { - Check_Type(argv[i], T_STRING); - id = rb_intern(RSTRING(argv[i])->ptr); - } - rb_export_method(self, id, ex); + rb_export_method(self, rb_to_id(argv[i]), ex); } } static VALUE -mod_public(argc, argv) +mod_public(argc, argv, module) int argc; VALUE *argv; + VALUE module; { - set_method_visibility(argc, argv, NOEX_PUBLIC); + set_method_visibility(module, argc, argv, NOEX_PUBLIC); return Qnil; } static VALUE -mod_private(argc, argv) +mod_private(argc, argv, module) int argc; VALUE *argv; + VALUE module; { - set_method_visibility(argc, argv, NOEX_PRIVATE); + set_method_visibility(module, argc, argv, NOEX_PRIVATE); return Qnil; } @@ -2775,19 +3239,13 @@ mod_modfunc(argc, argv, module) ID id; NODE *body, *old; - set_method_visibility(argc, argv, NOEX_PRIVATE); + set_method_visibility(module, argc, argv, NOEX_PRIVATE); for (i=0; i<argc; i++) { - if (FIXNUM_P(argv[i])) { - id = FIX2INT(argv[i]); - } - else { - Check_Type(argv[i], T_STRING); - id = rb_intern(RSTRING(argv[i])->ptr); - } + id = rb_to_id(argv[i]); body = search_method(module, id, 0); if (body == 0 || body->nd_body == 0) { - Fail("undefined method `%s' for module `%s'", - rb_id2name(id), rb_class2name(module)); + NameError("undefined method `%s' for module `%s'", + rb_id2name(id), rb_class2name(module)); } rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC); } @@ -2806,7 +3264,21 @@ mod_include(argc, argv, module) Check_Type(argv[i], T_MODULE); rb_include_module(module, argv[i]); } - return (VALUE)module; + return Qnil; +} + +VALUE /* moved from object.c for push_iter */ +class_s_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE obj = obj_alloc(class); + + PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT); + rb_funcall2(obj, init, argc, argv); + POP_ITER(); + return obj; } static VALUE @@ -2823,7 +3295,13 @@ obj_extend(argc, argv, obj) VALUE *argv; VALUE obj; { - return mod_include(argc, argv, rb_singleton_class(obj)); + int i; + + mod_include(argc, argv, rb_singleton_class(obj)); + for (i=0; i<argc; i++) { + rb_funcall(argv[i], rb_intern("object_extended"), 1, obj); + } + return Qnil; } void @@ -2833,16 +3311,66 @@ rb_extend_object(obj, module) rb_include_module(rb_singleton_class(obj), module); } -extern VALUE cKernel; extern VALUE cModule; VALUE f_trace_var(); VALUE f_untrace_var(); +extern VALUE rb_str_setter(); + +static VALUE +errat_setter(val, id, var) + VALUE val; + ID id; + VALUE *var; +{ + if (!NIL_P(val) && TYPE(val) != T_ARRAY) { + TypeError("value of $@ must be Array of String"); + } + return *var = val; +} + +static VALUE +f_catch(dmy, tag) +{ + NODE *state; + ID t; + VALUE val; + + t = rb_to_id(tag); + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + val = rb_yield(tag); + } + POP_TAG(); + if (state) { + if (state->nd_tag == TAG_THROW && state->nd_tlev == t) { + return state->nd_tval; + } + JUMP_TAG(state); + } + return val; +} + +static VALUE +f_throw(argc, argv) + int argc; + VALUE *argv; +{ + VALUE tag, value; + ID t; + + rb_scan_args(argc, argv, "11", &tag, &value); + t = rb_to_id(tag); + JUMP_TAG3(TAG_THROW, value, t); + /* not reached */ +} + void Init_eval() { - match = rb_intern("=~"); + init = rb_intern("initialize"); + eqq = rb_intern("==="); each = rb_intern("each"); aref = rb_intern("[]"); @@ -2851,25 +3379,49 @@ Init_eval() rb_global_variable(&top_scope); rb_global_variable(&eval_tree); rb_global_variable(&the_dyna_vars); - rb_define_private_method(cKernel, "exit", f_exit, -1); - rb_define_private_method(cKernel, "eval", f_eval, 1); + + rb_define_hooked_variable("$@", &errat, 0, errat_setter); + rb_define_hooked_variable("$!", &errinfo, 0, rb_str_setter); + + rb_define_private_method(cKernel, "eval", f_eval, -1); rb_define_private_method(cKernel, "iterator?", f_iterator_p, 0); rb_define_private_method(cKernel, "method_missing", f_missing, -1); rb_define_private_method(cKernel, "loop", f_loop, 0); + + rb_define_method(cKernel, "respond_to?", krn_respond_to, -1); + + rb_define_private_method(cKernel, "break", f_break, 0); + rb_define_alias(cKernel, "break!", "break"); + rb_define_private_method(cKernel, "next", f_next, 0); + rb_define_alias(cKernel, "next!", "next"); + rb_define_alias(cKernel, "continue", "next"); + rb_define_private_method(cKernel, "redo", f_redo, 0); + rb_define_alias(cKernel, "redo!", "redo"); + rb_define_private_method(cKernel, "retry", f_retry, 0); + rb_define_alias(cKernel, "retry!", "retry"); + rb_define_private_method(cKernel, "raise", f_raise, -1); + rb_define_alias(cKernel, "fail", "raise"); + rb_define_private_method(cKernel, "caller", f_caller, -1); + rb_define_private_method(cKernel, "exit", f_exit, -1); + + rb_define_private_method(cKernel, "catch", f_catch, 1); + rb_define_private_method(cKernel, "throw", f_throw, -1); + rb_define_method(cKernel, "send", f_send, -1); rb_define_method(cModule, "include", mod_include, -1); rb_define_method(cModule, "public", mod_public, -1); rb_define_method(cModule, "private", mod_private, -1); rb_define_method(cModule, "module_function", mod_modfunc, -1); + rb_define_method(cModule, "method_defined?", mod_method_defined, 1); rb_define_method(CLASS_OF(TopSelf), "include", top_include, -1); rb_define_method(cObject, "extend", obj_extend, -1); rb_define_private_method(cKernel, "trace_var", f_trace_var, -1); - rb_define_private_method(cKernel, "untrace_var", f_untrace_var, 1); + rb_define_private_method(cKernel, "untrace_var", f_untrace_var, -1); } VALUE f_autoload(); @@ -2909,8 +3461,6 @@ scope_dup(scope) } } -static ID blkdata; - static void blk_mark(data) struct BLOCK *data; @@ -2931,6 +3481,29 @@ blk_free(data) } static VALUE +f_binding(self) + VALUE self; +{ + extern VALUE cData; + struct BLOCK *data; + VALUE bind; + + PUSH_BLOCK(0,0); + bind = Make_Data_Struct(cData, struct BLOCK, blk_mark, blk_free, data); + MEMCPY(data, the_block, struct BLOCK, 1); + + data->iter = ITER_NOT; + data->frame.last_func = 0; + data->frame.argv = ALLOC_N(VALUE, data->frame.argc); + MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc); + + scope_dup(data->scope); + POP_BLOCK(); + + return bind; +} + +static VALUE proc_s_new(class) VALUE class; { @@ -2938,15 +3511,13 @@ proc_s_new(class) struct BLOCK *data; if (!iterator_p() && !f_iterator_p()) { - Fail("tryed to create Procedure-Object out of iterator"); + ArgError("tryed to create Procedure-Object out of iterator"); } - proc = obj_alloc(class); - - if (!blkdata) blkdata = rb_intern("blk"); - Make_Data_Struct(proc, blkdata, struct BLOCK, blk_mark, blk_free, data); - MEMCPY(data, the_block, struct BLOCK, 1); + proc = Make_Data_Struct(class, struct BLOCK, blk_mark, blk_free, data); + *data = *the_block; + data->iter = ITER_NOT; data->frame.argv = ALLOC_N(VALUE, data->frame.argc); MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc); @@ -2967,7 +3538,8 @@ proc_call(proc, args) { struct BLOCK *data; VALUE result = Qnil; - int state; + NODE *state; + int tag_level; if (TYPE(args) == T_ARRAY) { switch (RARRAY(args)->len) { @@ -2980,11 +3552,12 @@ proc_call(proc, args) } } - Get_Data_Struct(proc, blkdata, struct BLOCK, data); + Get_Data_Struct(proc, struct BLOCK, data); /* PUSH BLOCK from data */ PUSH_BLOCK2(data); PUSH_ITER(ITER_CUR); + the_frame->iter = ITER_CUR; PUSH_TAG(); state = EXEC_TAG(); @@ -2994,34 +3567,1047 @@ proc_call(proc, args) POP_TAG(); POP_ITER(); + tag_level = the_block->level; POP_BLOCK(); - switch (state) { - case 0: - break; - case TAG_BREAK: - case IN_BLOCK|TAG_BREAK: - Fail("break from block-closure"); - break; - case TAG_RETURN: - case IN_BLOCK|TAG_RETURN: - Fail("return from block-closure"); - break; - default: + if (state) { + if (data->scope && (data->scope->flag & SCOPE_NOSTACK)) { + /* orphan procedure */ + switch (state->nd_tag) { + case TAG_BREAK: /* never happen */ + break; + case IN_BLOCK|TAG_BREAK: + if (state->nd_tlev != tag_level) + Raise(eLocalJumpError, "break from proc-closure"); + break; + case TAG_RETRY: + Raise(eLocalJumpError, "retry from proc-closure"); + break; + case TAG_RETURN: /* never happen */ + case IN_BLOCK|TAG_RETURN: + Raise(eLocalJumpError, "return from proc-closure"); + break; + } + } + else { + state->nd_tag &= ~IN_BLOCK; + } JUMP_TAG(state); } - return result; } void Init_Proc() { - cProc = rb_define_class("Proc", cObject); + eLocalJumpError = rb_define_class("LocalJumpError", eException); + cProc = rb_define_class("Proc", cObject); rb_define_singleton_method(cProc, "new", proc_s_new, 0); rb_define_method(cProc, "call", proc_call, -2); - rb_define_private_method(cKernel, "lambda", f_lambda, 0); rb_define_private_method(cKernel, "proc", f_lambda, 0); + rb_define_private_method(cKernel, "lambda", f_lambda, 0); + rb_define_private_method(cKernel, "binding", f_binding, 0); } + +#ifdef THREAD + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +int thread_pending = 0; + +static VALUE cThread; + +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> +#include <errno.h> + +extern VALUE last_status; + +enum thread_status { + THREAD_RUNNABLE, + THREAD_STOPPED, + THREAD_TO_KILL, + THREAD_KILLED, +}; + +#define WAIT_FD (1<<0) +#define WAIT_TIME (1<<1) +#define WAIT_JOIN (1<<2) + +/* +infty, for this purpose */ +#define DELAY_INFTY 1E30 + +typedef struct thread * thread_t; + +struct thread { + struct thread *next, *prev; + jmp_buf context; + VALUE (*func)(); + void *arg; + + VALUE result; + + int stk_len; + int stk_max; + VALUE*stk_ptr; + VALUE*stk_pos; + + struct FRAME *frame; + struct SCOPE *scope; + struct RClass *class; + struct RVarmap *dyna_vars; + struct BLOCK *block; + struct iter *iter; + struct tag *tag; + + char *file; + int line; + + VALUE errat, errinfo; + VALUE last_status; + VALUE last_line; + VALUE last_match; + + enum thread_status status; + int wait_for; + int fd; + double delay; + thread_t join; + VALUE thread; +}; + +static thread_t curr_thread; +static int num_waiting_on_fd; +static int num_waiting_on_timer; +static int num_waiting_on_join; + +thread_curr() {return (int)curr_thread;} + +#define FOREACH_THREAD(x) x = curr_thread; do { x = x->next; +#define END_FOREACH(x) } while (x != curr_thread) + +/* Return the current time as a floating-point number */ +static double +timeofday() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6; +} + +static thread_t main_thread; + +#define ADJ(addr) (void*)(((VALUE*)(addr)-th->stk_pos)+th->stk_ptr) +#define STACK(addr) (th->stk_pos<(addr) && (addr)<th->stk_pos+th->stk_len) + +static void +thread_mark(th) + thread_t th; +{ + struct FRAME *frame; + struct BLOCK *block; + + gc_mark(th->result); + gc_mark_locations(th->stk_ptr, th->stk_ptr+th->stk_len); +#ifdef THINK_C + gc_mark_locations(th->stk_ptr+2, th->stk_ptr+th->stk_len+2); +#endif + gc_mark(th->thread); + if (th->join) gc_mark(th->join->thread); + + gc_mark(th->scope); + gc_mark(th->dyna_vars); + gc_mark(th->errat); + gc_mark(th->errinfo); + gc_mark(th->last_line); + gc_mark(th->last_match); + + /* mark data in copied stack */ + frame = th->frame; + while (frame && frame != top_frame) { + frame = ADJ(frame); + if (frame->argv && !STACK(frame->argv)) { + gc_mark_frame(frame); + } + frame = frame->prev; + } + block = th->block; + while (block) { + block = ADJ(block); + if (block->frame.argv && !STACK(block->frame.argv)) { + gc_mark_frame(&block->frame); + } + block = block->prev; + } +} + +void +gc_mark_threads() +{ + thread_t th; + + FOREACH_THREAD(th) { + thread_mark(th); + } END_FOREACH(th); +} + +static void +thread_free(th) + thread_t th; +{ + if (th->stk_ptr) free(th->stk_ptr); + th->stk_ptr = 0; +} + +static thread_t +thread_check(data) + struct RData *data; +{ + if (TYPE(data) != T_DATA || data->dfree != thread_free) { + TypeError("wrong argument type %s (expected Thread)", + rb_class2name(CLASS_OF(data))); + } + return (thread_t)data->data; +} + +VALUE lastline_get(); +void lastline_set(); +VALUE backref_get(); +void backref_set(); + +static int +thread_save_context(th) + thread_t th; +{ + VALUE v; + + th->stk_len = stack_length(); + th->stk_pos = (gc_stack_start<(VALUE*)&v)?gc_stack_start + :gc_stack_start - th->stk_len; + if (th->stk_len > th->stk_max) { + th->stk_max = th->stk_len; + REALLOC_N(th->stk_ptr, VALUE, th->stk_max); + } + FLUSH_REGISTER_WINDOWS; + MEMCPY(th->stk_ptr, th->stk_pos, VALUE, th->stk_len); + + th->frame = the_frame; + th->scope = the_scope; + th->class = the_class; + th->dyna_vars = the_dyna_vars; + th->block = the_block; + th->iter = the_iter; + th->tag = prot_tag; + th->errat = errat; + th->errinfo = errinfo; + th->last_status = last_status; + th->last_line = lastline_get(); + th->last_match = backref_get(); + + th->file = sourcefile; + th->line = sourceline; +} + +static void thread_restore_context(); + +static void +stack_extend(th, exit) + thread_t th; + int exit; +{ + VALUE space[1024]; + + memset(space, 0, 1); /* prevent array from optimization */ + thread_restore_context(th, exit); +} + +static void +thread_restore_context(th, exit) + thread_t th; + int exit; +{ + VALUE v; + static thread_t tmp; + static int ex; + + if (!th->stk_ptr) Bug("unsaved context"); + + if (&v < gc_stack_start) { + /* Stack grows downward */ + if (&v > th->stk_pos) stack_extend(th, exit); + } + else { + /* Stack grows upward */ + if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit); + } + + the_frame = th->frame; + the_scope = th->scope; + the_class = th->class; + the_dyna_vars = th->dyna_vars; + the_block = th->block; + the_iter = th->iter; + prot_tag = th->tag; + the_class = th->class; + errat = th->errat; + errinfo = th->errinfo; + last_status = th->last_status; + + lastline_set(th->last_line); + backref_set(th->last_match); + + sourcefile = th->file; + sourceline = th->line; + + tmp = th; + ex = exit; + FLUSH_REGISTER_WINDOWS; + MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len); + + switch (ex) { + case 1: + JUMP_TAG2(TAG_FATAL, INT2FIX(0)); + break; + + case 2: + rb_interrupt(); + break; + + default: + longjmp(tmp->context, 1); + } +} + +static void +thread_ready(th) + thread_t th; +{ + /* The thread is no longer waiting on anything */ + if (th->wait_for & WAIT_FD) { + num_waiting_on_fd--; + } + if (th->wait_for & WAIT_TIME) { + num_waiting_on_timer--; + } + if (th->wait_for & WAIT_JOIN) { + num_waiting_on_join--; + } + th->wait_for = 0; + th->status = THREAD_RUNNABLE; +} + +static void +thread_remove() +{ + thread_ready(curr_thread); + curr_thread->status = THREAD_KILLED; + curr_thread->prev->next = curr_thread->next; + curr_thread->next->prev = curr_thread->prev; + thread_schedule(); +} + +static int +thread_dead(th) + thread_t th; +{ + return th->status == THREAD_KILLED; +} + +void +thread_schedule() +{ + thread_t next; + thread_t th; + thread_t curr; + + thread_pending = 0; + if (curr_thread == curr_thread->next) return; + + next = 0; + curr = curr_thread; /* real current thread */ + + if (curr_thread->status == THREAD_KILLED) { + curr_thread = curr_thread->prev; + } + + again: + FOREACH_THREAD(th) { + if (th->status != THREAD_STOPPED && th->status != THREAD_KILLED) { + next = th; + break; + } + } + END_FOREACH(th); + + if (num_waiting_on_join) { + FOREACH_THREAD(th) { + if ((th->wait_for & WAIT_JOIN) && thread_dead(th->join)) { + th->join = 0; + th->wait_for &= ~WAIT_JOIN; + th->status = THREAD_RUNNABLE; + num_waiting_on_join--; + if (!next) next = th; + } + } + END_FOREACH(th); + } + + if (num_waiting_on_fd > 0 || num_waiting_on_timer > 0) { + fd_set readfds; + struct timeval delay_tv, *delay_ptr; + double delay, now; + + int n, max; + + do { + select_err: + max = 0; + FD_ZERO(&readfds); + if (num_waiting_on_fd > 0) { + FOREACH_THREAD(th) { + if (th->wait_for & WAIT_FD) { + FD_SET(th->fd, &readfds); + if (th->fd > max) max = th->fd; + } + } + END_FOREACH(th); + } + + delay = DELAY_INFTY; + if (num_waiting_on_timer > 0) { + now = timeofday(); + FOREACH_THREAD(th) { + if (th->wait_for & WAIT_TIME) { + if (th->delay <= now) { + th->delay = 0.0; + th->wait_for &= ~WAIT_TIME; + th->status = THREAD_RUNNABLE; + num_waiting_on_timer--; + next = th; + } else if (th->delay < delay) { + delay = th->delay; + } + } + } + END_FOREACH(th); + } + /* Do the select if needed */ + if (num_waiting_on_fd > 0 || !next) { + /* Convert delay to a timeval */ + /* If a thread is runnable, just poll */ + if (next) { + delay_tv.tv_sec = 0; + delay_tv.tv_usec = 0; + delay_ptr = &delay_tv; + } + else if (delay == DELAY_INFTY) { + delay_ptr = 0; + } + else { + delay -= now; + delay_tv.tv_sec = (unsigned int)delay; + delay_tv.tv_usec = (delay - (double)delay_tv.tv_sec) * 1e6; + delay_ptr = &delay_tv; + } + n = select(max+1, &readfds, 0, 0, delay_ptr); + if (n > 0) { + /* Some descriptors are ready. + Make the corresponding threads runnable. */ + FOREACH_THREAD(th) + if ((th->wait_for&WAIT_FD) + && FD_ISSET(th->fd, &readfds)) { + /* Wake up only one thread per fd. */ + FD_CLR(th->fd, &readfds); + th->status = THREAD_RUNNABLE; + th->fd = 0; + th->wait_for &= ~WAIT_FD; + num_waiting_on_fd--; + if (!next) next = th; /* Found one. */ + } + END_FOREACH(th); + } + if (n < 0 && !next) goto select_err; + } + /* The delays for some of the threads should have expired. + Go through the loop once more, to check the delays. */ + } while (!next && delay != DELAY_INFTY); + } + + if (!next) { + FOREACH_THREAD(th) { + fprintf(stderr, "%s:%d:deadlock 0x%x: %d:%d %s\n", + th->file, th->line, th->thread, th->status, + th->wait_for, th==main_thread?"(main)":""); + } + END_FOREACH(th); + Fatal("Thread: deadlock"); + } + if (next == curr) { + return; + } + + /* context switch */ + if (curr == curr_thread) { + thread_save_context(curr); + if (setjmp(curr->context)) { + return; + } + } + + curr_thread = next; + if (next->status == THREAD_TO_KILL) { + /* execute ensure-clause if any */ + thread_restore_context(next, 1); + } + thread_restore_context(next, 0); +} + +void +thread_wait_fd(fd) + int fd; +{ + if (curr_thread == curr_thread->next) return; + + curr_thread->status = THREAD_STOPPED; + curr_thread->fd = fd; + num_waiting_on_fd++; + curr_thread->wait_for |= WAIT_FD; + thread_schedule(); +} + +void +thread_fd_writable(fd) + int fd; +{ + struct timeval zero; + fd_set fds; + + zero.tv_sec = zero.tv_usec = 0; + if (curr_thread == curr_thread->next) return; + + for (;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + if (select(fd+1, 0, &fds, 0, &zero) == 1) break; + thread_schedule(); + } +} + +void +thread_wait_for(time) + struct timeval time; +{ + double date; + + if (curr_thread == curr_thread->next) { + int n; +#ifndef linux + double d, limit; + limit = timeofday()+(double)time.tv_sec+(double)time.tv_usec*1e-6; +#endif + for (;;) { + TRAP_BEG; + n = select(0, 0, 0, 0, &time); + TRAP_END; + if (n == 0) return; + +#ifndef linux + d = limit - timeofday(); + + time.tv_sec = (int)d; + time.tv_usec = (int)((d - (int)d)*1e6); + if (time.tv_usec < 0) { + time.tv_usec += 1e6; + time.tv_sec -= 1; + } + if (time.tv_sec < 0) return; +#endif + } + } + + date = timeofday() + (double)time.tv_sec + (double)time.tv_usec*1e-6; + curr_thread->status = THREAD_STOPPED; + curr_thread->delay = date; + num_waiting_on_timer++; + curr_thread->wait_for |= WAIT_TIME; + thread_schedule(); +} + +void thread_sleep(); + +int +thread_select(max, read, write, except, timeout) + int max; + fd_set *read, *write, *except; + struct timeval *timeout; +{ + double limit; + struct timeval zero; + fd_set r, *rp, w, *wp, x, *xp; + int n; + + if (!read && !write && !except) { + if (!timeout) { + thread_sleep(); + return; + } + thread_wait_for(*timeout); + return 0; + } + + if (timeout) { + limit = timeofday()+ + (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6; + } + + if (curr_thread == curr_thread->next) { /* no other thread */ +#ifndef linux + struct timeval tv, *tvp = timeout; + + if (timeout) { + tv = *timeout; + tvp = &tv; + } + for (;;) { + TRAP_BEG; + n = select(max, read, write, except, tvp); + TRAP_END; + if (n < 0 && errno == EINTR) { + if (timeout) { + double d = timeofday() - limit; + + tv.tv_sec = (unsigned int)d; + tv.tv_usec = (d - (double)tv.tv_sec) * 1e6; + } + continue; + } + return n; + } +#else + for (;;) { + TRAP_BEG; + n = select(max, read, write, except, timeout); + TRAP_END; + if (n < 0 && errno == EINTR) { + continue; + } + return n; + } +#endif + + } + + for (;;) { + zero.tv_sec = zero.tv_usec = 0; + if (read) {rp = &r; r = *read;} else {rp = 0;} + if (write) {wp = &w; w = *write;} else {wp = 0;} + if (except) {xp = &x; x = *except;} else {xp = 0;} + n = select(max, rp, wp, xp, &zero); + if (n > 0) { + /* write back fds */ + if (read) {*read = r;} + if (write) {*write = w;} + if (except) {*except = x;} + return n; + } + if (n < 0 && errno != EINTR) { + return n; + } + if (timeout) { + if (timeout->tv_sec == 0 && timeout->tv_usec == 0) return 0; + if (limit <= timeofday()) return 0; + } + + thread_schedule(); + CHECK_INTS; + } +} + +static VALUE +thread_join(dmy, data) + VALUE dmy; + struct RData *data; +{ + thread_t th = thread_check(data); + + if (thread_dead(th)) return Qnil; + if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread) + Fatal("Thread.join: deadlock"); + curr_thread->status = THREAD_STOPPED; + curr_thread->join = th; + num_waiting_on_join++; + curr_thread->wait_for |= WAIT_JOIN; + thread_schedule(); + + return Qnil; +} + +static VALUE +thread_current() +{ + return curr_thread->thread; +} + +int +th_cur() +{ + return (int)curr_thread; +} + +static VALUE +thread_run(data) + struct RData *data; +{ + thread_t th = thread_check(data); + + if (th->status == THREAD_KILLED) Fail("killed thread"); + thread_ready(th); + thread_schedule(); + + return (VALUE)data; +} + +static VALUE +thread_kill(data) + struct RData *data; +{ + thread_t th = thread_check(data); + + if (th->status == THREAD_TO_KILL) return Qnil; + if (th->status == THREAD_KILLED) return Qnil; + if (th == th->next || th == main_thread) rb_exit(0); + + thread_ready(th); + th->status = THREAD_TO_KILL; + thread_schedule(); + /* not reached */ +} + +static VALUE +thread_s_kill(obj, th) + VALUE obj, th; +{ + return thread_kill(th); +} + +static VALUE +thread_exit() +{ + return thread_kill(curr_thread->thread); +} + +static VALUE +thread_stop_method(data) + struct RData *data; +{ + thread_t th = thread_check(data); + + th->status = THREAD_STOPPED; + thread_schedule(); + + return Qnil; +} + +static void +thread_stop() +{ + thread_stop_method(curr_thread->thread); +} + +void +thread_sleep() +{ + if (curr_thread == curr_thread->next) { + TRAP_BEG; + sleep((32767<<16)+32767); + TRAP_END; + return; + } + thread_stop_method(curr_thread->thread); +} + +static thread_t +thread_alloc() +{ + thread_t th; + + th = ALLOC(struct thread); + th->status = THREAD_RUNNABLE; + th->func = 0; + th->arg = 0; + + th->status = 0; + th->result = 0; + th->errinfo = Qnil; + th->errat = Qnil; + + th->stk_ptr = 0; + th->stk_len = 0; + th->stk_max = 0; + th->wait_for = 0; + th->fd = 0; + th->delay = 0.0; + th->join = 0; + + th->frame = 0; + th->scope = 0; + th->class = 0; + th->dyna_vars = 0; + th->block = 0; + th->iter = 0; + th->tag = 0; + + th->thread = data_object_alloc(cThread, th, 0, thread_free); + + if (curr_thread) { + th->prev = curr_thread; + curr_thread->next->prev = th; + th->next = curr_thread->next; + curr_thread->next = th; + } + else { + curr_thread = th->prev = th->next = th; + th->status = THREAD_RUNNABLE; + } + + return th; +} + +VALUE +thread_create(fn, arg) + VALUE (*fn)(); + void *arg; +{ + thread_t th = thread_alloc(); + NODE *state; + + thread_save_context(curr_thread); + if (setjmp(curr_thread->context)) { + return th->thread; + } + + th->func = fn; + th->arg = arg; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + thread_save_context(th); + if (setjmp(th->context) == 0) { + curr_thread = th; + th->result = (*th->func)(th->arg, th); + } + } + POP_TAG(); + if (state && th->status != THREAD_TO_KILL) { + /* global exit within this thread */ + main_thread->errat = errat; + main_thread->errinfo = errinfo; + thread_cleanup(); + } + thread_remove(); +} + +static void +thread_yield(arg, th) + thread_t th; +{ + scope_dup(the_block->scope); + rb_yield(th->thread); +} + +static VALUE +thread_start() +{ + if (!iterator_p()) { + Raise(eLocalJumpError, "must be called as iterator"); + } + return thread_create(thread_yield, 0); +} + +static VALUE +thread_value(data) + struct RData *data; +{ + thread_t th = thread_check(data); + + thread_join(0, data); + return th->result; +} + +static VALUE +thread_status(data) + struct RData *data; +{ + thread_t th = thread_check(data); + + return thread_dead(th)?FALSE:TRUE; +} + +static VALUE +thread_stopped(data) + struct RData *data; +{ + thread_t th = thread_check(data); + + if (thread_dead(th)) return TRUE; + if (th->status == THREAD_STOPPED) return TRUE; + return FALSE; +} + +static void +thread_wait_other_threads() +{ + /* wait other threads to terminate */ + while (curr_thread != curr_thread->next) { + thread_schedule(); + } +} + +static void +thread_cleanup() +{ + thread_t th; + + FOREACH_THREAD(th) { + if (th != curr_thread && th->status != THREAD_KILLED) { + th->status = THREAD_TO_KILL; + th->wait_for = 0; + } + } + END_FOREACH(th); +} + +int thread_critical; + +static VALUE +thread_exclusive() +{ + NODE *state; + + thread_critical++; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_yield(Qnil); + } + POP_TAG(); + thread_critical--; + + if (state) JUMP_TAG(state); + thread_schedule(); + return Qnil; +} + +void +thread_interrupt() +{ + thread_t th = main_thread; + + thread_ready(main_thread); + if (th == curr_thread) { + rb_interrupt(); + } + curr_thread = main_thread; + thread_restore_context(curr_thread, 2); +} + +static thread_t loading_thread; +static int loading_nest; + +static int +thread_loading(feature) + char *feature; +{ + if (curr_thread != curr_thread->next && loading_thread) { + while (loading_thread != curr_thread) { + thread_schedule(); + CHECK_INTS; + } + if (rb_provided(feature)) return TRUE; /* no need to load */ + } + + loading_thread = curr_thread; + loading_nest++; + + return FALSE; +} + +static void +thread_loading_done() +{ + if (--loading_nest == 0) { + loading_thread = 0; + } +} + +#if defined(HAVE_SETITIMER) && !defined(__BOW__) +static void +catch_timer(sig) + int sig; +{ +#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL) + signal(sig, catch_timer); +#endif + if (!thread_critical) { + if (trap_immediate) { + trap_immediate = 0; + thread_schedule(); + } + else thread_pending = 1; + } +} +#else +int thread_tick = THREAD_TICK; +#endif + +void +Init_Thread() +{ + cThread = rb_define_class("Thread", cObject); + + rb_define_singleton_method(cThread, "new", thread_start, 0); + rb_define_singleton_method(cThread, "start", thread_start, 0); + rb_define_singleton_method(cThread, "fork", thread_start, 0); + + rb_define_singleton_method(cThread, "stop", thread_stop, 0); + rb_define_singleton_method(cThread, "kill", thread_s_kill, 1); + rb_define_singleton_method(cThread, "exit", thread_exit, 0); + rb_define_singleton_method(cThread, "pass", thread_schedule, 0); + rb_define_singleton_method(cThread, "join", thread_join, 1); + rb_define_singleton_method(cThread, "current", thread_current, 0); + rb_define_singleton_method(cThread, "exclusive", thread_exclusive, 0); + + rb_define_method(cThread, "run", thread_run, 0); + rb_define_method(cThread, "stop", thread_stop_method, 0); + rb_define_method(cThread, "exit", thread_kill, 0); + rb_define_method(cThread, "value", thread_value, 0); + rb_define_method(cThread, "status", thread_status, 0); + rb_define_method(cThread, "stop?", thread_stopped, 0); + rb_define_method(cThread, "stopped?", thread_stopped, 0); + + /* allocate main thread */ + main_thread = thread_alloc(); + +#if defined(HAVE_SETITIMER) && !defined(__BOW__) + { + struct itimerval tval; + +#ifdef POSIX_SIGNAL + posix_signal(SIGVTALRM, catch_timer); +#else + signal(SIGVTALRM, catch_timer); +#endif + + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 50000; + tval.it_value = tval.it_interval; + setitimer(ITIMER_VIRTUAL, &tval, NULL); + } +#endif +} +#endif |