From d2271cbdd18ea55851f21abd8862e5a3b6b98c9b Mon Sep 17 00:00:00 2001 From: nagai Date: Mon, 10 Jul 2006 09:52:30 +0000 Subject: * ext/tk/tcltklib.c: make SEGV risk lower at exit. * ext/tk/lib/tk.rb: ditto. * ext/tk/lib/multi-tk.rb: fail to call function-style methods on slave interpreters. The strategy (MultiTkIp_PseudoToplevel_Evaluable) to fix the problem is a little tricky. You may have to take care of conflicting with it. * ext/tk/lib/tk.rb: a little change for the pseudo-toplevel strategy. * ext/tk/lib/tk/font.rb: ditto. * ext/tk/lib/tk/msgcat.rb: ditto. * ext/tk/lib/tkextlib/itk/incr_tk.rb: ditto. * ext/tk/sample/demos-en/widget: fail to call function-style methods on sample scripts. To fix it, a strategy which similar to the way on MultiTiIp is used. Please take care when re-write and re-run a demo script on the Widget-Demo code viewer. * ext/tk/sample/demos-jp/widget: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@10505 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 26 ++++ ext/tk/lib/multi-tk.rb | 244 +++++++++++++++++++++++++++++++++++-- ext/tk/lib/tk.rb | 32 +++-- ext/tk/lib/tk/font.rb | 8 +- ext/tk/lib/tk/msgcat.rb | 3 +- ext/tk/lib/tkextlib/itk/incr_tk.rb | 3 +- ext/tk/sample/demos-en/widget | 103 ++++++++++++++-- ext/tk/sample/demos-jp/widget | 101 +++++++++++++-- ext/tk/tcltklib.c | 63 +++++++++- 9 files changed, 543 insertions(+), 40 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1331b6f4c6..2793ff185a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +Mon Jul 10 18:46:52 2006 Hidetoshi NAGAI + + * ext/tk/tcltklib.c: make SEGV risk lower at exit. + + * ext/tk/lib/tk.rb: ditto. + + * ext/tk/lib/multi-tk.rb: fail to call function-style methods on slave + interpreters. The strategy (MultiTkIp_PseudoToplevel_Evaluable) to + fix the problem is a little tricky. You may have to take care of + conflicting with it. + + * ext/tk/lib/tk.rb: a little change for the pseudo-toplevel strategy. + + * ext/tk/lib/tk/font.rb: ditto. + + * ext/tk/lib/tk/msgcat.rb: ditto. + + * ext/tk/lib/tkextlib/itk/incr_tk.rb: ditto. + + * ext/tk/sample/demos-en/widget: fail to call function-style methods + on sample scripts. To fix it, a strategy which similar to the way + on MultiTiIp is used. Please take care when re-write and re-run a + demo script on the Widget-Demo code viewer. + + * ext/tk/sample/demos-jp/widget: ditto. + Mon Jul 10 17:32:38 2006 Yukihiro Matsumoto * sample/test.rb: update test suites. diff --git a/ext/tk/lib/multi-tk.rb b/ext/tk/lib/multi-tk.rb index 42f92c3b55..645e5ca2ea 100644 --- a/ext/tk/lib/multi-tk.rb +++ b/ext/tk/lib/multi-tk.rb @@ -33,6 +33,64 @@ class << TclTkIp end +################################################ +# use pseudo-toplevel feature of MultiTkIp ? +if (!defined?(Use_PseudoToplevel_Feature_of_MultiTkIp) || + Use_PseudoToplevel_Feature_of_MultiTkIp) + module MultiTkIp_PseudoToplevel_Evaluable + #def pseudo_toplevel_eval(body = Proc.new) + # Thread.current[:TOPLEVEL] = self + # begin + # body.call + # ensure + # Thread.current[:TOPLEVEL] = nil + # end + #end + + def pseudo_toplevel_evaluable? + @pseudo_toplevel_evaluable + end + + def pseudo_toplevel_evaluable=(mode) + @pseudo_toplevel_evaluable = (mode)? true: false + end + + def self.extended(mod) + mod.__send__(:extend_object, mod) + mod.instance_variable_set('@pseudo_toplevel_evaluable', true) + end + end + + class Object + alias __method_missing_alias_for_MultiTkIp__ method_missing + private :__method_missing_alias_for_MultiTkIp__ + + def method_missing(id, *args) + begin + has_top = (top = MultiTkIp.__getip.__pseudo_toplevel) && + top.respond_to?(:pseudo_toplevel_evaluable?) && + top.pseudo_toplevel_evaluable? && + top.respond_to?(id) + rescue Exception => e + has_top = false + end + + if has_top + top.__send__(id, *args) + else + __method_missing_alias_for_MultiTkIp__(id, *args) + end + end + end +else + # dummy + module MultiTkIp_PseudoToplevel_Evaluable + def pseudo_toplevel_evaluable? + false + end + end +end + ################################################ # exceptiopn to treat the return value from IP class MultiTkIp_OK < Exception @@ -54,6 +112,8 @@ MultiTkIp_OK.freeze ################################################ # methods for construction class MultiTkIp + BASE_DIR = File.dirname(__FILE__) + @@SLAVE_IP_ID = ['slave'.freeze, '0'.taint].freeze @@IP_TABLE = {}.taint unless defined?(@@IP_TABLE) @@ -692,6 +752,46 @@ class MultiTkIp ################################# + @pseudo_toplevel = [false, nil] + + def self.__pseudo_toplevel + self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1] + end + + def self.__pseudo_toplevel=(m) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?) + @pseudo_toplevel[0] = true + @pseudo_toplevel[1] = m + else + fail ArgumentError, 'fail to set pseudo-toplevel' + end + self + end + + def self.__pseudo_toplevel_evaluable? + begin + @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable? + rescue Exception + false + end + end + + def self.__pseudo_toplevel_evaluable=(mode) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + @pseudo_toplevel[0] = (mode)? true: false + end + + ################################# + @assign_request = Class.new(Exception){ def self.new(target, ret) obj = super() @@ -746,10 +846,40 @@ class MultiTkIp ################################# + @init_ip_env_queue = Queue.new + Thread.new{ + current = Thread.current + loop { + mtx, ret, table, script = @init_ip_env_queue.deq + begin + ret[0] = table.each{|tg, ip| ip._init_ip_env(script) } + rescue Exception => e + ret[0] = e + ensure + mtx.unlock + end + } + } + + def self.__init_ip_env__(table, script) + ret = [] + mtx = Mutex.new.lock + @init_ip_env_queue.enq([mtx, ret, table, script]) + mtx.lock + if ret[0].kind_of?(Exception) + raise ret[0] + else + ret[0] + end + end + + ################################# + class << self undef :instance_eval end } + @@DEFAULT_MASTER.freeze # defend against modification ###################################### @@ -1115,6 +1245,8 @@ class MultiTkIp @threadgroup = ThreadGroup.new + @pseudo_toplevel = [false, nil] + @cmd_queue = Queue.new =begin @@ -1424,6 +1556,17 @@ class MultiTkIp } end + def _remove_tk_procs(*names) + return if slave? + names.each{|name| + name = name.to_s + @interp._invoke('rename', name, '') + @interp._invoke('interp', 'slaves').split.each{|slave| + @interp._invoke('interp', 'alias', slave, name, '') rescue nil + } + } + end + def _init_ip_internal(init_ip_env, add_tk_procs) #init_ip_env.each{|script| self.eval_proc{script.call(self)}} init_ip_env.each{|script| self._init_ip_env(script)} @@ -1450,9 +1593,21 @@ class MultiTkIp __getip._tk_table_list[id] end def self.create_table - #if __getip.slave? - # raise SecurityError, "slave-IP has no permission creating a new table" - #end + if __getip.slave? + begin + raise SecurityError, "slave-IP has no permission creating a new table" + rescue SecurityError => e + #p e.backtrace + # Is called on a Ruby/Tk library? + caller_info = e.backtrace[1] + if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:} + # Probably, caller is a Ruby/Tk library --> allow creating + else + raise e + end + end + end + id = @@TK_TABLE_LIST.size obj = Object.new @@TK_TABLE_LIST << obj @@ -1468,15 +1623,48 @@ class MultiTkIp def self.init_ip_env(script = Proc.new) @@INIT_IP_ENV << script - @@IP_TABLE.each{|tg, ip| - ip._init_ip_env(script) - } + if __getip.slave? + begin + raise SecurityError, "slave-IP has no permission initializing IP env" + rescue SecurityError => e + #p e.backtrace + # Is called on a Ruby/Tk library? + caller_info = e.backtrace[1] + if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:} + # Probably, caller is a Ruby/Tk library --> allow creating + else + raise e + end + end + end + + # @@IP_TABLE.each{|tg, ip| + # ip._init_ip_env(script) + # } + @@DEFAULT_MASTER.__init_ip_env__(@@IP_TABLE, script) end def self.add_tk_procs(name, args=nil, body=nil) - @@ADD_TK_PROCS << [name, args, body] + if name.kind_of?(Array) # => an array of [name, args, body] + name.each{|param| self.add_tk_procs(*param)} + else + name = name.to_s + @@ADD_TK_PROCS << [name, args, body] + @@IP_TABLE.each{|tg, ip| + ip._add_tk_procs(name, args, body) + } + end + end + + def self.remove_tk_procs(*names) + names.each{|name| + name = name.to_s + @@ADD_TK_PROCS.delete_if{|elem| + elem.kind_of?(Array) && elem[0].to_s == name + } + } @@IP_TABLE.each{|tg, ip| - ip._add_tk_procs(name, args, body) + ip._remove_tk_procs(*names) } end @@ -1565,6 +1753,46 @@ class MultiTkIp end +# pseudo-toplevel operation support +class MultiTkIp + # instance method + def __pseudo_toplevel + self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1] + end + + def __pseudo_toplevel=(m) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?) + @pseudo_toplevel[0] = true + @pseudo_toplevel[1] = m + else + fail ArgumentError, 'fail to set pseudo-toplevel' + end + self + end + + def __pseudo_toplevel_evaluable? + begin + @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable? + rescue Exception + false + end + end + + def __pseudo_toplevel_evaluable=(mode) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + @pseudo_toplevel[0] = (mode)? true: false + end +end + # evaluate a procedure on the proper interpreter class MultiTkIp # instance method diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb index 4504d1d2ec..32360ac144 100644 --- a/ext/tk/lib/tk.rb +++ b/ext/tk/lib/tk.rb @@ -1192,8 +1192,22 @@ module TkCore script.call(self) end def INTERP.add_tk_procs(name, args = nil, body = nil) - @add_tk_procs << [name, args, body] - self._invoke('proc', name, args, body) if args && body + if name.kind_of?(Array) + name.each{|param| self.add_tk_procs(*param)} + else + name = name.to_s + @add_tk_procs << [name, args, body] + self._invoke('proc', name, args, body) if args && body + end + end + def INTERP.remove_tk_procs(*names) + names.each{|name| + name = name.to_s + @add_tk_procs.delete_if{|elem| + elem.kind_of?(Array) && elem[0].to_s == name + } + self._invoke('rename', name, '') + } end def INTERP.init_ip_internal ip = self @@ -1285,6 +1299,8 @@ module TkCore EOL =end + at_exit{ INTERP.remove_tk_procs(TclTkLib::FINALIZE_PROC_NAME) } + EventFlag = TclTkLib::EventFlag def callback_break @@ -3886,12 +3902,14 @@ class TkObject= 8.4 alias showVars showVars2 end +# Pseudo-Toplevel support +module PseudoToplevel_Evaluable + def pseudo_toplevel_eval(body = Proc.new) + Thread.current[:TOPLEVEL] = self + begin + body.call + ensure + Thread.current[:TOPLEVEL] = nil + end + end + + def pseudo_toplevel_evaluable? + @pseudo_toplevel_evaluable + end + def pseudo_toplevel_evaluable=(mode) + @pseudo_toplevel_evaluable = (mode)? true: false + end + + def self.extended(mod) + mod.__send__(:extend_object, mod) + mod.instance_variable_set('@pseudo_toplevel_evaluable', true) + end +end + +class Object + alias __method_missing__ method_missing + private :__method_missing__ + + def method_missing(id, *args) + begin + has_top = (top = Thread.current[:TOPLEVEL]) && + top.respond_to?(:pseudo_toplevel_evaluable?) && + top.pseudo_toplevel_evaluable? && + top.respond_to?(id) + rescue Exception => e + has_top = false + end + + if has_top + top.__send__(id, *args) + else + __method_missing__(id, *args) + end + end +end + +class Proc + def initialize(*args, &b) + super + @__pseudo_toplevel__ = Thread.current[:TOPLEVEL] + end + + alias __call__ call + def call(*args, &b) + if top = @__pseudo_toplevel__ + orig_top = Thread.current[:TOPLEVEL] + Thread.current[:TOPLEVEL] = top + begin + __call__(*args, &b) + ensure + Thread.current[:TOPLEVEL] = orig_top + end + else + __call__(*args, &b) + end + end +end + +def proc(&b) + Proc.new(&b) +end +def lambda(&b) + Proc.new(&b) +end + def _null_binding + Module.new.instance_eval{extend PseudoToplevel_Evaluable} # binding - Module.new.instance_eval{binding} + # Module.new.instance_eval{binding} end private :_null_binding +def eval_samplecode(code) + Thread.new{ _null_binding.pseudo_toplevel_eval{ eval(code) } }.run + Tk.update +end + # invoke -- # This procedure is called when the user clicks on a demo description. # It is responsible for invoking the demonstration. @@ -529,8 +610,9 @@ def invoke(txt, idx) cursor = txt.cget('cursor') txt.cursor('watch') Tk.update - eval(IO.readlines("#{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb").join, _null_binding) - Tk.update + # eval(IO.readlines("#{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb").join, _null_binding) + # Tk.update + eval_samplecode(IO.readlines("#{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb").join) txt.cursor(cursor) $tag_visited.add("#{idx} linestart +1 chars", "#{idx} lineend +1 chars") @@ -596,7 +678,8 @@ def showCode1(demo) }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2) TkButton.new(f) { text "Rerun Demo" - command proc{eval($code_text.get('1.0','end'), _null_binding)} + # command proc{eval($code_text.get('1.0','end'), _null_binding)} + command proc{eval_samplecode($code_text.get('1.0','end'))} }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2) # f.pack('side'=>'bottom', 'expand'=>'yes', 'fill'=>'x') f.pack('side'=>'bottom', 'fill'=>'x') @@ -690,7 +773,8 @@ def showCode2(demo) :image=>$image['print'], :compound=>:left) b_run = TkButton.new(bf, :text=>'Rerun Demo', :command=>proc{ - eval($code_text.get('1.0','end'), _null_binding) + # eval($code_text.get('1.0','end'), _null_binding) + eval_samplecode($code_text.get('1.0','end')) }, :image=>$image['refresh'], :compound=>:left) @@ -812,7 +896,7 @@ end # def aboutBox Tk.messageBox('icon'=>'info', 'type'=>'ok', 'title'=>'About Widget Demo', - 'message'=>"Ruby/Tk widget demonstration Ver.1.5.6-en\n\n" + + 'message'=>"Ruby/Tk widget demonstration Ver.1.6.0-en\n\n" + "based on demos of Tk8.1 -- 8.5 " + "( Copyright:: " + "(c) 1996-1997 Sun Microsystems, Inc. / " + @@ -837,11 +921,12 @@ ARGV.each{|cmd| if cmd =~ /(.*).rb/ cmd = $1 end - eval(IO.readlines("#{[$demo_dir, cmd].join(File::Separator)}.rb").join, - _null_binding) + #eval(IO.readlines("#{[$demo_dir, cmd].join(File::Separator)}.rb").join, + # _null_binding) + eval_samplecode(IO.readlines("#{[$demo_dir, cmd].join(File::Separator)}.rb").join) } if no_launcher - $root.withdraw # hide root window + $root.withdraw # hide root window Thread.start{ loop do count = 0 diff --git a/ext/tk/sample/demos-jp/widget b/ext/tk/sample/demos-jp/widget index 2156f841b7..11495dda54 100644 --- a/ext/tk/sample/demos-jp/widget +++ b/ext/tk/sample/demos-jp/widget @@ -551,12 +551,93 @@ else # ver >= 8.4 alias showVars showVars2 end +# 疑似トップレベルサポート +module PseudoToplevel_Evaluable + def pseudo_toplevel_eval(body = Proc.new) + Thread.current[:TOPLEVEL] = self + begin + body.call + ensure + Thread.current[:TOPLEVEL] = nil + end + end + + def pseudo_toplevel_evaluable? + @pseudo_toplevel_evaluable + end + def pseudo_toplevel_evaluable=(mode) + @pseudo_toplevel_evaluable = (mode)? true: false + end + + def self.extended(mod) + mod.__send__(:extend_object, mod) + mod.instance_variable_set('@pseudo_toplevel_evaluable', true) + end +end + +class Object + alias __method_missing__ method_missing + private :__method_missing__ + + def method_missing(id, *args) + begin + has_top = (top = Thread.current[:TOPLEVEL]) && + top.respond_to?(:pseudo_toplevel_evaluable?) && + top.pseudo_toplevel_evaluable? && + top.respond_to?(id) + rescue Exception => e + has_top = false + end + + if has_top + top.__send__(id, *args) + else + __method_missing__(id, *args) + end + end +end + +class Proc + def initialize(*args, &b) + super + @__pseudo_toplevel__ = Thread.current[:TOPLEVEL] + end + + alias __call__ call + def call(*args, &b) + if top = @__pseudo_toplevel__ + orig_top = Thread.current[:TOPLEVEL] + Thread.current[:TOPLEVEL] = top + begin + __call__(*args, &b) + ensure + Thread.current[:TOPLEVEL] = orig_top + end + else + __call__(*args, &b) + end + end +end + +def proc(&b) + Proc.new(&b) +end +def lambda(&b) + Proc.new(&b) +end + def _null_binding + Module.new.instance_eval{extend PseudoToplevel_Evaluable} # binding - Module.new.instance_eval{binding} + # Module.new.instance_eval{binding} end private :_null_binding +def eval_samplecode(code) + Thread.new{ _null_binding.pseudo_toplevel_eval{ eval(code) } }.run + Tk.update +end + # テキスト上での click に対する動作 def invoke(txt, idx) tag = txt.tag_names(idx).find{|t| t.kind_of?(String) && t =~ /^demo-/} @@ -565,8 +646,9 @@ def invoke(txt, idx) cursor = txt.cget('cursor') txt.cursor('watch') Tk.update - eval(IO.readlines("#{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb").join, _null_binding) - Tk.update + # eval(IO.readlines("#{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb").join, _null_binding) + # Tk.update + eval_samplecode(IO.readlines("#{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb").join) txt.cursor(cursor) $tag_visited.add("#{idx} linestart +1 chars", "#{idx} lineend +1 chars") @@ -622,7 +704,8 @@ def showCode1(demo) }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2) TkButton.new(f) { text "再実行" - command proc{eval($code_text.get('1.0','end'), _null_binding)} + # command proc{eval($code_text.get('1.0','end'), _null_binding)} + command proc{eval_samplecode($code_text.get('1.0','end'))} }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2) # f.pack('side'=>'bottom', 'expand'=>'yes', 'fill'=>'x') f.pack('side'=>'bottom', 'fill'=>'x') @@ -716,7 +799,8 @@ def showCode2(demo) :image=>$image['print'], :compound=>:left) b_run = TkButton.new(bf, :text=>'再実行', :command=>proc{ - eval($code_text.get('1.0','end'), _null_binding) + # eval($code_text.get('1.0','end'), _null_binding) + eval_samplecode($code_text.get('1.0','end')) }, :image=>$image['refresh'], :compound=>:left) @@ -842,7 +926,7 @@ end # def aboutBox Tk.messageBox('icon'=>'info', 'type'=>'ok', 'title'=>'About Widget Demo', - 'message'=>"Ruby/Tk ウィジェットデモ Ver.1.5.6-jp\n\n" + + 'message'=>"Ruby/Tk ウィジェットデモ Ver.1.6.0-jp\n\n" + "based on demos of Tk8.1 -- 8.5 " + "( Copyright:: " + "(c) 1996-1997 Sun Microsystems, Inc. / " + @@ -867,8 +951,9 @@ ARGV.each{|cmd| if cmd =~ /(.*).rb/ cmd = $1 end - eval(IO.readlines("#{[$demo_dir, cmd].join(File::Separator)}.rb").join, - _null_binding) + #eval(IO.readlines("#{[$demo_dir, cmd].join(File::Separator)}.rb").join, + # _null_binding) + eval_samplecode(IO.readlines("#{[$demo_dir, cmd].join(File::Separator)}.rb").join) } if no_launcher $root.withdraw # hide root window diff --git a/ext/tk/tcltklib.c b/ext/tk/tcltklib.c index 2ec4233d47..5644dc98f2 100644 --- a/ext/tk/tcltklib.c +++ b/ext/tk/tcltklib.c @@ -4,7 +4,7 @@ * Oct. 24, 1997 Y. Matsumoto */ -#define TCLTKLIB_RELEASE_DATE "2006-07-03" +#define TCLTKLIB_RELEASE_DATE "2006-07-10" #include "ruby.h" #include "rubysig.h" @@ -81,6 +81,8 @@ static char *finalize_hook_name = "INTERP_FINALIZE_HOOK"; static void ip_finalize _((Tcl_Interp*)); +static int at_exit = 0; + /* for callback break & continue */ static VALUE eTkCallbackReturn; @@ -4358,6 +4360,33 @@ delete_slaves(ip) /* finalize operation */ +static VALUE +lib_mark_at_exit(self) + VALUE self; +{ + at_exit = 1; + return Qnil; +} + +static int +#if TCL_MAJOR_VERSION >= 8 +ip_null_proc(clientData, interp, argc, argv) + ClientData clientData; + Tcl_Interp *interp; + int argc; + Tcl_Obj *CONST argv[]; +#else /* TCL_MAJOR_VERSION < 8 */ +ip_null_proc(clientData, interp, argc, argv) + ClientData clientData; + Tcl_Interp *interp; + int argc; + char *argv[]; +#endif +{ + Tcl_ResetResult(interp); + return TCL_OK; +} + static void ip_finalize(ip) Tcl_Interp *ip; @@ -4403,6 +4432,29 @@ ip_finalize(ip) /* delete slaves */ delete_slaves(ip); + /* shut off some connections from Tcl-proc to Ruby */ + if (at_exit) { + /* NOTE: Only when at exit. + Because, ruby removes objects, which depends on the deleted + interpreter, on some callback operations. + It is important for GC. */ +#if TCL_MAJOR_VERSION >= 8 + Tcl_CreateObjCommand(ip, "ruby", ip_null_proc, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); + Tcl_CreateObjCommand(ip, "ruby_eval", ip_null_proc, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); + Tcl_CreateObjCommand(ip, "ruby_cmd", ip_null_proc, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); +#else /* TCL_MAJOR_VERSION < 8 */ + Tcl_CreateCommand(ip, "ruby", ip_null_proc, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); + Tcl_CreateCommand(ip, "ruby_eval", ip_null_proc, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); + Tcl_CreateCommand(ip, "ruby_cmd", ip_null_proc, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); +#endif + } + /* delete root widget */ #if 0 DUMP1("check `destroy'"); @@ -4425,7 +4477,7 @@ ip_finalize(ip) /* call finalize-hook-proc */ DUMP1("check `finalize-hook-proc'"); - if (Tcl_GetCommandInfo(ip, finalize_hook_name, &info)) { + if ( Tcl_GetCommandInfo(ip, finalize_hook_name, &info)) { DUMP2("call finalize hook proc '%s'", finalize_hook_name); ruby_debug = Qfalse; ruby_verbose = Qnil; @@ -4680,7 +4732,6 @@ ip_CallWhenDeleted(clientData, ip) rb_thread_critical = thr_crit_bup; } - /* initialize interpreter */ static VALUE ip_init(argc, argv, self) @@ -7925,6 +7976,8 @@ Init_tcltklib() /* --------------------------------------------------------------- */ + rb_define_module_function(lib, "_mark_at_exit", lib_mark_at_exit, 0); + rb_define_module_function(lib, "mainloop", lib_mainloop, -1); rb_define_module_function(lib, "mainloop_thread?", lib_evloop_thread_p, 0); @@ -8061,6 +8114,10 @@ Init_tcltklib() /* --------------------------------------------------------------- */ + rb_eval_string("at_exit{ TclTkLib._mark_at_exit }"); + + /* --------------------------------------------------------------- */ + ret = ruby_open_tcl_dll(rb_argv0 ? RSTRING(rb_argv0)->ptr : 0); switch(ret) { case TCLTK_STUBS_OK: -- cgit v1.2.3