トップ «前の日記(2009-02-11) 最新 次の日記(2009-02-13)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2009-02-12

_ ASRでCOMのネストした呼び出しがうまくいかない

YARVの動きに詳しい方、助言をお願いします。

以下、状況を手短に説明。

ASRのテストをしていたら、イベントハンドラ付きのテキストを設定するところでエラーとなる。

たとえば以下のコード

Window.document.getElementById('foo').innerHTML = '<div id="x" onclick="funcall()">hello</div>'

この呼び出しを行うと、次の動作をする。スレッドは2つある。1つはMSHTAのスレッド、もう1つはRubyのスレッドだ。それぞれスレッドMとスレッドRとする。

  • 上のコードをASRが作成した無名モジュールに対してinstance_evalする(スレッドR)。
  • WIN32OLEオブジェクト(Windowやdocument、foo要素など)のメソッド呼び出しの都度、スレッドMへLRPCが起きる。
  • DOM(スレッドM)が、onclickを発見し、ASRに対して、"funcall()"という文字列のParseScriptProcedureを呼び出す。
  • ASRはその文字列を以下のように変形する。_unk = Proc.new { funcall() }(スレッドM)
  • 上で作成した文字列を引数として無名モジュールのinstance_evalを呼ぶ(スレッドR)
  • この時点で、Ruby VMが「Can't eval on top of Fiber or Thread」でrb_raiseする。

これは困った。Ruby-1.8.7では特に問題なかったのだが。

原因は、おそらくWin32OLE#method_missing呼び出し中(innerHTML=メソッドの呼び出し)の再入にあるのだろう。

この時のスレッドRのスタックトレースは以下となる。

cfp->iseq == 0
   直前のcfp
         cfp->iseq = 0
         cfp->flag 0x0061 (& VM_FRAME_FLAG_PASSED == 0)
vm_get_ruby_level_caller_cfp(rb_thread_struct * 0x001b6ff0, rb_control_frame_t * 0x029bfed0) line 152
eval_string_with_cref(unsigned long 0x02f23cc4, unsigned long 0x02fff1e8, unsigned long 0x00000004, RNode * 0x02fff1c0, const char * 0x03000518, int 0x00000000) line 707 + 16 bytes
eval_under(unsigned long 0x02f23c60, unsigned long 0x02f23cc4, unsigned long 0x02fff1e8, const char * 0x03000518, int 0x00000000) line 941 + 27 bytes
specific_eval(int 0x00000003, unsigned long * 0x032dc934, unsigned long 0x02f23c60, unsigned long 0x02f23cc4) line 979 + 27 bytes
rb_obj_instance_eval(int 0x00000003, unsigned long * 0x032dc934, unsigned long 0x02f23cc4) line 1017 + 21 bytes
call_cfunc(unsigned long (void)* 0x02c394a0 rb_obj_instance_eval(int, unsigned long *, unsigned long), unsigned long 0x02f23cc4, int 0xffffffff, int 0x00000003, const unsigned long * 0x032dc934) line 286 + 15 bytes
vm_call0(rb_thread_struct * 0x001b6ff0, unsigned long 0x02f4bf44, unsigned long 0x02f23cc4, unsigned long 0x00000ad0, unsigned long 0x00000ad0, int 0x00000003, const unsigned long * 0x032dc934, const RNode * 0x02f449b0, int 0x00000000) line 71 + 31 bytes
rb_funcall2(unsigned long 0x02f23cc4, unsigned long 0x00000ad0, int 0x00000003, const unsigned long * 0x032dc934) line 412 + 1290 bytes
safe_funcall(unsigned long 0x032dc92c) line 142 + 27 bytes
rb_protect(unsigned long (unsigned long)* 0x10034840 safe_funcall(unsigned long), unsigned long 0x032dc92c, int * 0x032dc940) line 647 + 31 bytes
CRubyWrapper::rb_funcall_with_string2(CRubyWrapper * const 0x030610f8, IRubyEngine * 0x03063ac8, unsigned long 0x02f23cc4, unsigned long 0x00000ad0, long 0x00000000, long 0x00000029, unsigned char * 0x0056834c, unsigned char 0x00, tagVARIANT * 0x032dcbc8 {VT_EMPTY}, IActiveScriptError * * 0x00571e90) line 243 + 18 bytes
RPCRT4! 75ee43b6()
RPCRT4! 75f70cc6()
RPCRT4! 75f7093e()    <----LRPCによってスレッドMから再入
OLE32! 77389759()
OLE32! 773896f3()
OLE32! 772a9d67()
OLE32! 772a9c5c()
OLE32! 772aaab0()
OLE32! 7738961b()
OLE32! 77389498()
OLE32! 77389aa2()
OLE32! 772aa8ea()
OLE32! 772aa8a9()
USER32! 763a8807()
USER32! 763a8962()
USER32! 763a8aad()
USER32! 763a8b00()
OLE32! 7727b0a6()
OLE32! 77278a9d()
OLE32! 772789d7()
OLE32! 7727898f()
OLE32! 7727ac88()
OLE32! 7727ad74()
OLE32! 77387c28()
OLE32! 773889d4()
OLE32! 7727ad2e()
OLE32! 7727ace0()
OLE32! 7729e688()
RPCRT4! 75f71074()      <---  LRPCでスレッドMのDOM操作
RPCRT4! 75f7102b()
DISPEX! 727f2576()
DISPEX! 727f2a43()
CRScriptCore::fole_propertyput(unsigned long 0x001b85dc, unsigned long 0x02fff1fc, unsigned long 0x02fff210) line 866 + 54 bytes
CRScriptCore::foleex_missing(int 0x00000002, unsigned long * 0x032ddf60, unsigned long 0x001b85dc) line 1233 + 22 bytes
call_cfunc(unsigned long (void)* 0x100012b2 CRScriptCore::foleex_missing(int,unsigned long *,unsigned long), unsigned long 0x001b85dc, int 0xffffffff, int 0x00000002, const unsigned long * 0x032ddf60) line 286 + 15 bytes
vm_call0(rb_thread_struct * 0x001b6ff0, unsigned long 0x02f23ecc, unsigned long 0x001b85dc, unsigned long 0x00000188, unsigned long 0x00000188, int 0x00000002, const unsigned long * 0x032ddf60, const RNode * 0x02f23e54, int 0x00000000) line 71 + 31 bytes
rb_funcall2(unsigned long 0x001b85dc, unsigned long 0x00000188, int 0x00000002, const unsigned long * 0x032ddf60) line 412 + 1290 bytes
vm_method_missing(rb_thread_struct * 0x001b6ff0, unsigned long 0x000031e4, unsigned long 0x001b85dc, int 0x00000001, rb_block_struct * 0x00000000, int 0x00000000) line 418 + 25 bytes
vm_exec_core(rb_thread_struct * 0x001b6ff0, unsigned long 0x00000000) line 999 + 1881 bytes
vm_exec(rb_thread_struct * 0x001b6ff0) line 1077 + 13 bytes
rb_vm_invoke_proc(rb_thread_struct * 0x001b6ff0, rb_proc_t * 0x02fd9460, unsigned long 0x02f23cc4, int 0x00000001, const unsigned long * 0x032def50, rb_block_struct * 0x00000000) line 571 + 1117 bytes
proc_call(int 0x00000001, unsigned long * 0x032def50, unsigned long 0x001b87d0) line 525 + 33 bytes
call_cfunc(unsigned long (void)* 0x02c9cb30 proc_call(int, unsigned long *, unsigned long), unsigned long 0x001b87d0, int 0xffffffff, int 0x00000001, const unsigned long * 0x032def50) line 286 + 15 bytes
vm_call0(rb_thread_struct * 0x001b6ff0, unsigned long 0x02f2f628, unsigned long 0x001b87d0, unsigned long 0x000015a0, unsigned long 0x000015a0, int 0x00000001, const unsigned long * 0x032def50, const RNode * 0x02f2f5b0, int 0x00000000) line 71 + 31 bytes
rb_funcall2(unsigned long 0x001b87d0, unsigned long 0x000015a0, int 0x00000001, const unsigned long * 0x032def50) line 412 + 1290 bytes
CRubyWrapper::InvokeRuby(unsigned long 0x032def40) line 513 + 33 bytes
rb_protect(unsigned long (unsigned long)* 0x10001af0 CRubyWrapper::InvokeRuby(unsigned long), unsigned long 0x032def40, int * 0x032defb0) line 647 + 31 bytes
CRubyWrapper::rb_invoke(CRubyWrapper * const 0x030610f8, IRubyEngine * 0x03063ac8, unsigned long 0x001b87d0, unsigned long 0x000015a0, tagDISPPARAMS * 0x0056f9b8, tagVARIANT * 0x005975d0 {VT_EMPTY}, IActiveScriptError * * 0x00597608) line 346 + 18 bytes
RPCRT4! 75ee43b6()
RPCRT4! 75f70cc6()
RPCRT4! 75f7093e()                <--- LRPCでスレッドRからASRのCOM wrapper経由でinstance_evalが呼ばれる
OLE32! 77389759()
OLE32! 773896f3()
OLE32! 772a9d67()
OLE32! 772a9c5c()
OLE32! 772aaab0()
OLE32! 7738961b()
OLE32! 77389498()
OLE32! 77389aa2()
OLE32! 772aa8ea()
OLE32! 772aa8a9()
USER32! 763a8807()
USER32! 763a8962()
USER32! 763a8aad()
USER32! 763a8b00()
RubyWrapper(void * 0x0033e694) line 141 + 12 bytes
_threadstartex(void * 0x03063f20) line 227 + 13 bytes    <--- 本当のトップレベル
KERNEL32! 7743e3f3()
NTDLL! 77cecfed()
NTDLL! 77ced1ff()

途中でLRPCやOLEがスタックを変えてはいるが、YARVのフレームやスレッドを破壊しているわけではない(実際、thおよびcfpはASRのinstance_eval呼び出し時はWin32OLE#method_missingの時のものを取り出している。したがって状態はcall_cfunc中にcall_cfuncされた状態だと思える、というかどういう状態が正しいのかYARVを読み切れていない)。

c呼び出しが重なるのが原因かと思って小さな再現プログラムを作ったが、その方法では再現しない。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|

ジェズイットを見習え