1# 2# multi-tk.rb - supports multi Tk interpreters 3# by Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp> 4 5require 'tcltklib' 6require 'tkutil' 7require 'thread' 8 9if defined? Tk 10 fail RuntimeError,"'multi-tk' library must be required before requiring 'tk'" 11end 12 13################################################ 14# ignore exception on the mainloop? 15 16TclTkLib.mainloop_abort_on_exception = true 17# TclTkLib.mainloop_abort_on_exception = false 18# TclTkLib.mainloop_abort_on_exception = nil 19 20 21################################################ 22# add ThreadGroup check to TclTkIp.new 23class << TclTkIp 24 alias __new__ new 25 private :__new__ 26 27 def new(*args) 28 if Thread.current.group != ThreadGroup::Default 29 raise SecurityError, 'only ThreadGroup::Default can call TclTkIp.new' 30 end 31 obj = __new__(*args) 32 obj.instance_eval{ 33 @force_default_encoding ||= TkUtil.untrust([false]) 34 @encoding ||= TkUtil.untrust([nil]) 35 def @encoding.to_s; self.join(nil); end 36 } 37 obj 38 end 39end 40 41 42################################################ 43# exceptiopn to treat the return value from IP 44class MultiTkIp_OK < Exception 45 def self.send(thread, ret=nil) 46 thread.raise self.new(ret) 47 end 48 49 def initialize(ret=nil) 50 super('succeed') 51 @return_value = ret 52 end 53 54 attr_reader :return_value 55 alias value return_value 56end 57MultiTkIp_OK.freeze 58 59 60################################################ 61# methods for construction 62class MultiTkIp 63 class Command_Queue < Queue 64 def initialize(interp) 65 @interp = interp 66 super() 67 end 68 69 def push(value) 70 if !@interp || @interp.deleted? 71 fail RuntimeError, "Tk interpreter is already deleted" 72 end 73 super(value) 74 end 75 alias << push 76 alias enq push 77 78 def close 79 @interp = nil 80 end 81 end 82 Command_Queue.freeze 83 84 BASE_DIR = File.dirname(__FILE__) 85 86 WITH_RUBY_VM = Object.const_defined?(:RubyVM) && ::RubyVM.class == Class 87 WITH_ENCODING = defined?(::Encoding.default_external) 88 #WITH_ENCODING = Object.const_defined?(:Encoding) && ::Encoding.class == Class 89 90 (@@SLAVE_IP_ID = ['slave'.freeze, TkUtil.untrust('0')]).instance_eval{ 91 @mutex = Mutex.new 92 def mutex; @mutex; end 93 freeze 94 } 95 96 @@IP_TABLE = TkUtil.untrust({}) unless defined?(@@IP_TABLE) 97 98 @@INIT_IP_ENV = TkUtil.untrust([]) unless defined?(@@INIT_IP_ENV) # table of Procs 99 @@ADD_TK_PROCS = TkUtil.untrust([]) unless defined?(@@ADD_TK_PROCS) # table of [name, args, body] 100 101 @@TK_TABLE_LIST = TkUtil.untrust([]) unless defined?(@@TK_TABLE_LIST) 102 103 unless defined?(@@TK_CMD_TBL) 104 @@TK_CMD_TBL = TkUtil.untrust(Object.new) 105 106 # @@TK_CMD_TBL.instance_variable_set('@tbl', {}.taint) 107 tbl_obj = TkUtil.untrust(Hash.new{|hash,key| 108 fail IndexError, "unknown command ID '#{key}'" 109 }) 110 @@TK_CMD_TBL.instance_variable_set('@tbl', tbl_obj) 111 112 class << @@TK_CMD_TBL 113 allow = [ 114 '__send__', '__id__', 'freeze', 'inspect', 'kind_of?', 'object_id', 115 '[]', '[]=', 'delete', 'each', 'has_key?' 116 ] 117 instance_methods.each{|m| undef_method(m) unless allow.index(m.to_s)} 118 119 def kind_of?(klass) 120 @tbl.kind_of?(klass) 121 end 122 123 def inspect 124 if Thread.current.group == ThreadGroup::Default 125 @tbl.inspect 126 else 127 ip = MultiTkIp.__getip 128 @tbl.reject{|idx, ent| ent.respond_to?(:ip) && ent.ip != ip}.inspect 129 end 130 end 131 132 def [](idx) 133 return unless (ent = @tbl[idx]) 134 if Thread.current.group == ThreadGroup::Default 135 ent 136 elsif ent.respond_to?(:ip) 137 (ent.ip == MultiTkIp.__getip)? ent: nil 138 else 139 ent 140 end 141 end 142 143 def []=(idx,val) 144 if self.has_key?(idx) && Thread.current.group != ThreadGroup::Default 145 fail SecurityError,"cannot change the entried command" 146 end 147 @tbl[idx] = val 148 end 149 150 def delete(idx, &blk) 151 # if gets an entry, is permited to delete 152 if self[idx] 153 @tbl.delete(idx) 154 elsif blk 155 blk.call(idx) 156 else 157 nil 158 end 159 end 160 161 def each(&blk) 162 if Thread.current.group == ThreadGroup::Default 163 @tbl.each(&blk) 164 else 165 ip = MultiTkIp.__getip 166 @tbl.each{|idx, ent| 167 blk.call(idx, ent) unless ent.respond_to?(:ip) && ent.ip != ip 168 } 169 end 170 self 171 end 172 173 def has_key?(k) 174 @tbl.has_key?(k) 175 end 176 alias include? has_key? 177 alias key? has_key? 178 alias member? has_key? 179 end 180 181 @@TK_CMD_TBL.freeze 182 end 183 184 ###################################### 185 186 @@CB_ENTRY_CLASS = Class.new(TkCallbackEntry){ 187 def initialize(ip, cmd) 188 @ip = ip 189 @safe = safe = $SAFE 190 # @cmd = cmd 191 cmd = MultiTkIp._proc_on_safelevel(&cmd) 192 @cmd = proc{|*args| cmd.call(safe, *args)} 193 self.freeze 194 end 195 attr_reader :ip, :cmd 196 def inspect 197 cmd.inspect 198 end 199 def call(*args) 200 unless @ip.deleted? 201 current = Thread.current 202 backup_ip = current[:callback_ip] 203 current[:callback_ip] = @ip 204 begin 205 ret = @ip.cb_eval(@cmd, *args) 206 fail ret if ret.kind_of?(Exception) 207 ret 208 rescue TkCallbackBreak, TkCallbackContinue => e 209 fail e 210 rescue SecurityError => e 211 # in 'exit', 'exit!', and 'abort' : security error --> delete IP 212 if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/ 213 @ip.delete 214 elsif @ip.safe? 215 if @ip.respond_to?(:cb_error) 216 @ip.cb_error(e) 217 else 218 nil # ignore 219 end 220 else 221 fail e 222 end 223 rescue Exception => e 224 fail e if e.message =~ /^TkCallback/ 225 226 if @ip.safe? 227 if @ip.respond_to?(:cb_error) 228 @ip.cb_error(e) 229 else 230 nil # ignore 231 end 232 else 233 fail e 234 end 235 ensure 236 current[:callback_ip] = backup_ip 237 end 238 end 239 end 240 }.freeze 241 242 ###################################### 243 244 def _keys2opts(src_keys) 245 return nil if src_keys == nil 246 keys = {}; src_keys.each{|k, v| keys[k.to_s] = v} 247 #keys.collect{|k,v| "-#{k} #{v}"}.join(' ') 248 keys.collect{|k,v| "-#{k} #{TclTkLib._conv_listelement(TkComm::_get_eval_string(v))}"}.join(' ') 249 end 250 private :_keys2opts 251 252 def _check_and_return(thread, exception, wait=0) 253 unless thread 254 unless exception.kind_of?(MultiTkIp_OK) 255 msg = "#{exception.class}: #{exception.message}" 256 257 if @interp.deleted? 258 warn("Warning (#{self}): " + msg) 259 return nil 260 end 261 262 if safe? 263 warn("Warning (#{self}): " + msg) if $DEBUG 264 return nil 265 end 266 267 begin 268 @interp._eval_without_enc(@interp._merge_tklist('bgerror', msg)) 269 rescue Exception => e 270 warn("Warning (#{self}): " + msg) 271 end 272 end 273 return nil 274 end 275 276 if wait == 0 277 # no wait 278 Thread.pass 279 if thread.stop? 280 thread.raise exception 281 end 282 return thread 283 end 284 285 # wait to stop the caller thread 286 wait.times{ 287 if thread.stop? 288 # ready to send exception 289 thread.raise exception 290 return thread 291 end 292 293 # wait 294 Thread.pass 295 } 296 297 # unexpected error 298 thread.raise RuntimeError, "the thread may not wait for the return value" 299 return thread 300 end 301 302 ###################################### 303 304 def set_cb_error(cmd = Proc.new) 305 @cb_error_proc[0] = cmd 306 end 307 308 def cb_error(e) 309 if @cb_error_proc[0].respond_to?(:call) 310 @cb_error_proc[0].call(e) 311 end 312 end 313 314 ###################################### 315 316 def set_safe_level(safe) 317 if safe > @safe_level[0] 318 @safe_level[0] = safe 319 @cmd_queue.enq([@system, 'set_safe_level', safe]) 320 end 321 @safe_level[0] 322 end 323 def safe_level=(safe) 324 set_safe_level(safe) 325 end 326 def self.set_safe_level(safe) 327 __getip.set_safe_level(safe) 328 end 329 def self.safe_level=(safe) 330 self.set_safe_level(safe) 331 end 332 def safe_level 333 @safe_level[0] 334 end 335 def self.safe_level 336 __getip.safe_level 337 end 338 339 def wait_on_mainloop? 340 @wait_on_mainloop[0] 341 end 342 def wait_on_mainloop=(bool) 343 @wait_on_mainloop[0] = bool 344 end 345 346 def running_mainloop? 347 @wait_on_mainloop[1] > 0 348 end 349 350 def _destroy_slaves_of_slaveIP(ip) 351 unless ip.deleted? 352 # ip._split_tklist(ip._invoke('interp', 'slaves')).each{|name| 353 ip._split_tklist(ip._invoke_without_enc('interp', 'slaves')).each{|name| 354 name = _fromUTF8(name) 355 begin 356 # ip._eval_without_enc("#{name} eval {foreach i [after info] {after cancel $i}}") 357 after_ids = ip._eval_without_enc("#{name} eval {after info}") 358 ip._eval_without_enc("#{name} eval {foreach i {#{after_ids}} {after cancel $i}}") 359 rescue Exception 360 end 361 begin 362 # ip._invoke('interp', 'eval', name, 'destroy', '.') 363 ip._invoke(name, 'eval', 'destroy', '.') 364 rescue Exception 365 end 366 367 # safe_base? 368 if ip._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' 369 begin 370 ip._eval_without_enc("::safe::interpDelete #{name}") 371 rescue Exception 372 end 373 end 374=begin 375 if ip._invoke('interp', 'exists', name) == '1' 376 begin 377 ip._invoke(name, 'eval', 'exit') 378 rescue Exception 379 end 380 end 381=end 382 unless ip.deleted? 383 if ip._invoke('interp', 'exists', name) == '1' 384 begin 385 ip._invoke('interp', 'delete', name) 386 rescue Exception 387 end 388 end 389 end 390 } 391 end 392 end 393 394 def _receiver_eval_proc_core(safe_level, thread, cmd, *args) 395 begin 396 #ret = proc{$SAFE = safe_level; cmd.call(*args)}.call 397 #ret = cmd.call(safe_level, *args) 398 normal_ret = false 399 ret = catch(:IRB_EXIT) do # IRB hack 400 retval = cmd.call(safe_level, *args) 401 normal_ret = true 402 retval 403 end 404 unless normal_ret 405 # catch IRB_EXIT 406 exit(ret) 407 end 408 ret 409 rescue SystemExit => e 410 # delete IP 411 unless @interp.deleted? 412 @slave_ip_tbl.each{|name, subip| 413 _destroy_slaves_of_slaveIP(subip) 414 begin 415 # subip._eval_without_enc("foreach i [after info] {after cancel $i}") 416 after_ids = subip._eval_without_enc("after info") 417 subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") 418 rescue Exception 419 end 420=begin 421 begin 422 subip._invoke('destroy', '.') unless subip.deleted? 423 rescue Exception 424 end 425=end 426 # safe_base? 427 if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' 428 begin 429 @interp._eval_without_enc("::safe::interpDelete #{name}") 430 rescue Exception 431 else 432 next if subip.deleted? 433 end 434 end 435 if subip.respond_to?(:safe_base?) && subip.safe_base? && 436 !subip.deleted? 437 # do 'exit' to call the delete_hook procedure 438 begin 439 subip._eval_without_enc('exit') 440 rescue Exception 441 end 442 else 443 begin 444 subip.delete unless subip.deleted? 445 rescue Exception 446 end 447 end 448 } 449 450 begin 451 # @interp._eval_without_enc("foreach i [after info] {after cancel $i}") 452 after_ids = @interp._eval_without_enc("after info") 453 @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") 454 rescue Exception 455 end 456 begin 457 @interp._invoke('destroy', '.') unless @interp.deleted? 458 rescue Exception 459 end 460 if @safe_base && !@interp.deleted? 461 # do 'exit' to call the delete_hook procedure 462 @interp._eval_without_enc('exit') 463 else 464 @interp.delete unless @interp.deleted? 465 end 466 end 467 468 if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/ 469 _check_and_return(thread, MultiTkIp_OK.new($3 == 'exit')) 470 else 471 _check_and_return(thread, MultiTkIp_OK.new(nil)) 472 end 473 474 # if master? && !safe? && allow_ruby_exit? 475 if !@interp.deleted? && master? && !safe? && allow_ruby_exit? 476=begin 477 ObjectSpace.each_object(TclTkIp){|obj| 478 obj.delete unless obj.deleted? 479 } 480=end 481 #exit(e.status) 482 fail e 483 end 484 # break 485 486 rescue SecurityError => e 487 # in 'exit', 'exit!', and 'abort' : security error --> delete IP 488 if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/ 489 ret = ($3 == 'exit') 490 unless @interp.deleted? 491 @slave_ip_tbl.each{|name, subip| 492 _destroy_slaves_of_slaveIP(subip) 493 begin 494 # subip._eval_without_enc("foreach i [after info] {after cancel $i}") 495 after_ids = subip._eval_without_enc("after info") 496 subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") 497 rescue Exception 498 end 499=begin 500 begin 501 subip._invoke('destroy', '.') unless subip.deleted? 502 rescue Exception 503 end 504=end 505 # safe_base? 506 if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' 507 begin 508 @interp._eval_without_enc("::safe::interpDelete #{name}") 509 rescue Exception 510 else 511 next if subip.deleted? 512 end 513 end 514 if subip.respond_to?(:safe_base?) && subip.safe_base? && 515 !subip.deleted? 516 # do 'exit' to call the delete_hook procedure 517 begin 518 subip._eval_without_enc('exit') 519 rescue Exception 520 end 521 else 522 begin 523 subip.delete unless subip.deleted? 524 rescue Exception 525 end 526 end 527 } 528 529 begin 530 # @interp._eval_without_enc("foreach i [after info] {after cancel $i}") 531 after_ids = @interp._eval_without_enc("after info") 532 @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") 533 rescue Exception 534 end 535=begin 536 begin 537 @interp._invoke('destroy', '.') unless @interp.deleted? 538 rescue Exception 539 end 540=end 541 if @safe_base && !@interp.deleted? 542 # do 'exit' to call the delete_hook procedure 543 @interp._eval_without_enc('exit') 544 else 545 @interp.delete unless @interp.deleted? 546 end 547 end 548 _check_and_return(thread, MultiTkIp_OK.new(ret)) 549 # break 550 551 else 552 # raise security error 553 _check_and_return(thread, e) 554 end 555 556 rescue Exception => e 557 # raise exception 558 begin 559 bt = _toUTF8(e.backtrace.join("\n")) 560 if MultiTkIp::WITH_ENCODING 561 bt.force_encoding('utf-8') 562 else 563 bt.instance_variable_set(:@encoding, 'utf-8') 564 end 565 rescue Exception 566 bt = e.backtrace.join("\n") 567 end 568 begin 569 @interp._set_global_var('errorInfo', bt) 570 rescue Exception 571 end 572 _check_and_return(thread, e) 573 574 else 575 # no exception 576 _check_and_return(thread, MultiTkIp_OK.new(ret)) 577 end 578 end 579 580 def _receiver_eval_proc(last_thread, safe_level, thread, cmd, *args) 581 if thread 582 Thread.new{ 583 last_thread.join if last_thread 584 unless @interp.deleted? 585 _receiver_eval_proc_core(safe_level, thread, cmd, *args) 586 end 587 } 588 else 589 Thread.new{ 590 unless @interp.deleted? 591 _receiver_eval_proc_core(safe_level, thread, cmd, *args) 592 end 593 } 594 last_thread 595 end 596 end 597 598 private :_receiver_eval_proc, :_receiver_eval_proc_core 599 600 def _receiver_mainloop(check_root) 601 if @evloop_thread[0] && @evloop_thread[0].alive? 602 @evloop_thread[0] 603 else 604 @evloop_thread[0] = Thread.new{ 605 while !@interp.deleted? 606 #if check_root 607 # inf = @interp._invoke_without_enc('info', 'command', '.') 608 # break if !inf.kind_of?(String) || inf != '.' 609 #end 610 break if check_root && !@interp.has_mainwindow? 611 sleep 0.5 612 end 613 } 614 @evloop_thread[0] 615 end 616 end 617 618 def _create_receiver_and_watchdog(lvl = $SAFE) 619 lvl = $SAFE if lvl < $SAFE 620 621 # command-procedures receiver 622 receiver = Thread.new(lvl){|safe_level| 623 last_thread = {} 624 625 loop do 626 break if @interp.deleted? 627 thread, cmd, *args = @cmd_queue.deq 628 if thread == @system 629 # control command 630 case cmd 631 when 'set_safe_level' 632 begin 633 safe_level = args[0] if safe_level < args[0] 634 rescue Exception 635 end 636 when 'call_mainloop' 637 thread = args.shift 638 _check_and_return(thread, 639 MultiTkIp_OK.new(_receiver_mainloop(*args))) 640 else 641 # ignore 642 end 643 644 else 645 # procedure 646 last_thread[thread] = _receiver_eval_proc(last_thread[thread], 647 safe_level, thread, 648 cmd, *args) 649 end 650 end 651 } 652 653 # watchdog of receiver 654 watchdog = Thread.new{ 655 begin 656 loop do 657 sleep 1 658 if @interp.deleted? 659 receiver.kill 660 @cmd_queue.close 661 end 662 break unless receiver.alive? 663 end 664 rescue Exception 665 # ignore all kind of Exception 666 end 667 668 # receiver is dead 669 retry_count = 3 670 loop do 671 Thread.pass 672 begin 673 thread, cmd, *args = @cmd_queue.deq(true) # non-block 674 rescue ThreadError 675 # queue is empty 676 retry_count -= 1 677 break if retry_count <= 0 678 sleep 0.5 679 retry 680 end 681 next unless thread 682 if thread.alive? 683 if @interp.deleted? 684 thread.raise RuntimeError, 'the interpreter is already deleted' 685 else 686 thread.raise RuntimeError, 687 'the interpreter no longer receives command procedures' 688 end 689 end 690 end 691 } 692 693 # return threads 694 [receiver, watchdog] 695 end 696 private :_check_and_return, :_create_receiver_and_watchdog 697 698 ###################################### 699 700 unless self.const_defined? :RUN_EVENTLOOP_ON_MAIN_THREAD 701 ### Ruby 1.9 !!!!!!!!!!!!!!!!!!!!!!!!!! 702 RUN_EVENTLOOP_ON_MAIN_THREAD = false 703 end 704 705 if self.const_defined? :DEFAULT_MASTER_NAME 706 name = DEFAULT_MASTER_NAME.to_s 707 else 708 name = nil 709 end 710 if self.const_defined?(:DEFAULT_MASTER_OPTS) && 711 DEFAULT_MASTER_OPTS.kind_of?(Hash) 712 keys = DEFAULT_MASTER_OPTS 713 else 714 keys = {} 715 end 716 717 @@DEFAULT_MASTER = self.allocate 718 @@DEFAULT_MASTER.instance_eval{ 719 @tk_windows = TkUtil.untrust({}) 720 721 @tk_table_list = TkUtil.untrust([]) 722 723 @slave_ip_tbl = TkUtil.untrust({}) 724 725 @slave_ip_top = TkUtil.untrust({}) 726 727 @evloop_thread = TkUtil.untrust([]) 728 729 unless keys.kind_of? Hash 730 fail ArgumentError, "expecting a Hash object for the 2nd argument" 731 end 732 733 if !WITH_RUBY_VM || RUN_EVENTLOOP_ON_MAIN_THREAD ### check Ruby 1.9 !!!!!!! 734 @interp = TclTkIp.new(name, _keys2opts(keys)) 735 else ### Ruby 1.9 !!!!!!!!!!! 736 @interp_thread = Thread.new{ 737 current = Thread.current 738 begin 739 current[:interp] = interp = TclTkIp.new(name, _keys2opts(keys)) 740 rescue e 741 current[:interp] = e 742 raise e 743 end 744 #sleep 745 current[:mutex] = mutex = Mutex.new 746 current[:root_check] = cond_var = ConditionVariable.new 747 748 status = [nil] 749 def status.value 750 self[0] 751 end 752 def status.value=(val) 753 self[0] = val 754 end 755 current[:status] = status 756 757 begin 758 begin 759 #TclTkLib.mainloop_abort_on_exception = false 760 #Thread.current[:status].value = TclTkLib.mainloop(true) 761 interp.mainloop_abort_on_exception = true 762 current[:status].value = interp.mainloop(true) 763 rescue SystemExit=>e 764 current[:status].value = e 765 rescue Exception=>e 766 current[:status].value = e 767 retry if interp.has_mainwindow? 768 ensure 769 mutex.synchronize{ cond_var.broadcast } 770 end 771 772 #Thread.current[:status].value = TclTkLib.mainloop(false) 773 current[:status].value = interp.mainloop(false) 774 775 ensure 776 # interp must be deleted before the thread for interp is dead. 777 # If not, raise Tcl_Panic on Tcl_AsyncDelete because async handler 778 # deleted by the wrong thread. 779 interp.delete 780 end 781 } 782 until @interp_thread[:interp] 783 Thread.pass 784 end 785 # INTERP_THREAD.run 786 raise @interp_thread[:interp] if @interp_thread[:interp].kind_of? Exception 787 @interp = @interp_thread[:interp] 788 789 # delete the interpreter and kill the eventloop thread at exit 790 interp = @interp 791 interp_thread = @interp_thread 792 END{ 793 if interp_thread.alive? 794 interp.delete 795 interp_thread.kill 796 end 797 } 798 799 def self.mainloop(check_root = true) 800 begin 801 TclTkLib.set_eventloop_window_mode(true) 802 @interp_thread.value 803 ensure 804 TclTkLib.set_eventloop_window_mode(false) 805 end 806 end 807 end 808 809 @interp.instance_eval{ 810 @force_default_encoding ||= TkUtil.untrust([false]) 811 @encoding ||= TkUtil.untrust([nil]) 812 def @encoding.to_s; self.join(nil); end 813 } 814 815 @ip_name = nil 816 817 @callback_status = TkUtil.untrust([]) 818 819 @system = Object.new 820 821 @wait_on_mainloop = TkUtil.untrust([true, 0]) 822 823 @threadgroup = Thread.current.group 824 825 @safe_base = false 826 827 @safe_level = [$SAFE] 828 829 @cmd_queue = MultiTkIp::Command_Queue.new(@interp) 830 831 @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog(@safe_level[0]) 832 833 @threadgroup.add @cmd_receiver 834 @threadgroup.add @receiver_watchdog 835 836 # NOT enclose @threadgroup for @@DEFAULT_MASTER 837 838 @@IP_TABLE[ThreadGroup::Default] = self 839 @@IP_TABLE[@threadgroup] = self 840 841 ################################# 842 843 @pseudo_toplevel = [false, nil] 844 845 def self.__pseudo_toplevel 846 Thread.current.group == ThreadGroup::Default && 847 MultiTkIp.__getip == @@DEFAULT_MASTER && 848 self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1] 849 end 850 851 def self.__pseudo_toplevel=(m) 852 unless (Thread.current.group == ThreadGroup::Default && 853 MultiTkIp.__getip == @@DEFAULT_MASTER) 854 fail SecurityError, "no permission to manipulate" 855 end 856 857 # if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?) 858 if m.respond_to?(:pseudo_toplevel_evaluable?) 859 @pseudo_toplevel[0] = true 860 @pseudo_toplevel[1] = m 861 else 862 fail ArgumentError, 'fail to set pseudo-toplevel' 863 end 864 self 865 end 866 867 def self.__pseudo_toplevel_evaluable? 868 begin 869 @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable? 870 rescue Exception 871 false 872 end 873 end 874 875 def self.__pseudo_toplevel_evaluable=(mode) 876 unless (Thread.current.group == ThreadGroup::Default && 877 MultiTkIp.__getip == @@DEFAULT_MASTER) 878 fail SecurityError, "no permission to manipulate" 879 end 880 881 @pseudo_toplevel[0] = (mode)? true: false 882 end 883 884 ################################# 885 886 @assign_request = Class.new(Exception){ 887 def self.new(target, ret) 888 obj = super() 889 obj.target = target 890 obj.ret = ret 891 obj 892 end 893 attr_accessor :target, :ret 894 } 895 896 @assign_thread = Thread.new{ 897 loop do 898 begin 899 Thread.stop 900 rescue @assign_request=>req 901 begin 902 req.ret[0] = req.target.instance_eval{ 903 @cmd_receiver, @receiver_watchdog = 904 _create_receiver_and_watchdog(@safe_level[0]) 905 @threadgroup.add @cmd_receiver 906 @threadgroup.add @receiver_watchdog 907 @threadgroup.enclose 908 true 909 } 910 rescue Exception=>e 911 begin 912 req.ret[0] = e 913 rescue Exception 914 # ignore 915 end 916 end 917 rescue Exception 918 # ignore 919 end 920 end 921 } 922 923 def self.assign_receiver_and_watchdog(target) 924 ret = [nil] 925 @assign_thread.raise(@assign_request.new(target, ret)) 926 while ret[0] == nil 927 unless @assign_thread.alive? 928 raise RuntimeError, 'lost the thread to assign a receiver and a watchdog thread' 929 end 930 end 931 if ret[0].kind_of?(Exception) 932 raise ret[0] 933 else 934 ret[0] 935 end 936 end 937 938 ################################# 939 940 @init_ip_env_queue = Queue.new 941 Thread.new{ 942 current = Thread.current 943 loop { 944 mtx, cond, ret, table, script = @init_ip_env_queue.deq 945 begin 946 ret[0] = table.each{|tg, ip| ip._init_ip_env(script) } 947 rescue Exception => e 948 ret[0] = e 949 ensure 950 mtx.synchronize{ cond.signal } 951 end 952 mtx = cond = ret = table = script = nil # clear variables for GC 953 } 954 } 955 956 def self.__init_ip_env__(table, script) 957 ret = [] 958 mtx = (Thread.current[:MultiTk_ip_Mutex] ||= Mutex.new) 959 cond = (Thread.current[:MultiTk_ip_CondVar] ||= ConditionVariable.new) 960 mtx.synchronize{ 961 @init_ip_env_queue.enq([mtx, cond, ret, table, script]) 962 cond.wait(mtx) 963 } 964 if ret[0].kind_of?(Exception) 965 raise ret[0] 966 else 967 ret[0] 968 end 969 end 970 971 ################################# 972 973 class << self 974 undef :instance_eval 975 end 976 } 977 978 @@DEFAULT_MASTER.freeze # defend against modification 979 980 ###################################### 981 982 def self.inherited(subclass) 983 # trust if on ThreadGroup::Default or @@DEFAULT_MASTER's ThreadGroup 984 if @@IP_TABLE[Thread.current.group] == @@DEFAULT_MASTER 985 begin 986 class << subclass 987 self.methods.each{|m| 988 name = m.to_s 989 begin 990 unless name == '__id__' || name == '__send__' || name == 'freeze' 991 undef_method(m) 992 end 993 rescue Exception 994 # ignore all exceptions 995 end 996 } 997 end 998 ensure 999 subclass.freeze 1000 fail SecurityError, 1001 "cannot create subclass of MultiTkIp on a untrusted ThreadGroup" 1002 end 1003 end 1004 end 1005 1006 ###################################### 1007 1008 @@SAFE_OPT_LIST = [ 1009 'accessPath'.freeze, 1010 'statics'.freeze, 1011 'nested'.freeze, 1012 'deleteHook'.freeze 1013 ].freeze 1014 1015 def _parse_slaveopts(keys) 1016 name = nil 1017 safe = false 1018 safe_opts = {} 1019 tk_opts = {} 1020 1021 keys.each{|k,v| 1022 k_str = k.to_s 1023 if k_str == 'name' 1024 name = v 1025 elsif k_str == 'safe' 1026 safe = v 1027 elsif @@SAFE_OPT_LIST.member?(k_str) 1028 safe_opts[k_str] = v 1029 else 1030 tk_opts[k_str] = v 1031 end 1032 } 1033 1034 if keys['without_tk'] || keys[:without_tk] 1035 [name, safe, safe_opts, nil] 1036 else 1037 [name, safe, safe_opts, tk_opts] 1038 end 1039 end 1040 private :_parse_slaveopts 1041 1042 def _create_slave_ip_name 1043 @@SLAVE_IP_ID.mutex.synchronize{ 1044 name = @@SLAVE_IP_ID.join('') 1045 @@SLAVE_IP_ID[1].succ! 1046 name.freeze 1047 } 1048 end 1049 private :_create_slave_ip_name 1050 1051 ###################################### 1052 1053 def __check_safetk_optkeys(optkeys) 1054 # based on 'safetk.tcl' 1055 new_keys = {} 1056 optkeys.each{|k,v| new_keys[k.to_s] = v} 1057 1058 # check 'display' 1059 if !new_keys.key?('display') 1060 begin 1061 #new_keys['display'] = @interp._invoke('winfo screen .') 1062 new_keys['display'] = @interp._invoke('winfo', 'screen', '.') 1063 rescue 1064 if ENV[DISPLAY] 1065 new_keys['display'] = ENV[DISPLAY] 1066 elsif !new_keys.key?('use') 1067 warn "Warning: no screen info or ENV[DISPLAY], so use ':0.0'" 1068 new_keys['display'] = ':0.0' 1069 end 1070 end 1071 end 1072 1073 # check 'use' 1074 if new_keys.key?('use') 1075 # given 'use' 1076 case new_keys['use'] 1077 when TkWindow 1078 new_keys['use'] = TkWinfo.id(new_keys['use']) 1079 #assoc_display = @interp._eval('winfo screen .') 1080 assoc_display = @interp._invoke('winfo', 'screen', '.') 1081 when /^\..*/ 1082 new_keys['use'] = @interp._invoke('winfo', 'id', new_keys['use']) 1083 assoc_display = @interp._invoke('winfo', 'screen', new_keys['use']) 1084 else 1085 begin 1086 pathname = @interp._invoke('winfo', 'pathname', new_keys['use']) 1087 assoc_display = @interp._invoke('winfo', 'screen', pathname) 1088 rescue 1089 assoc_display = new_keys['display'] 1090 end 1091 end 1092 1093 # match display? 1094 if assoc_display != new_keys['display'] 1095 if optkeys.key?(:display) || optkeys.key?('display') 1096 fail RuntimeError, 1097 "conflicting 'display'=>#{new_keys['display']} " + 1098 "and display '#{assoc_display}' on 'use'=>#{new_keys['use']}" 1099 else 1100 new_keys['display'] = assoc_display 1101 end 1102 end 1103 end 1104 1105 # return 1106 new_keys 1107 end 1108 private :__check_safetk_optkeys 1109 1110 def __create_safetk_frame(slave_ip, slave_name, app_name, keys) 1111 # display option is used by ::safe::loadTk 1112 loadTk_keys = {} 1113 loadTk_keys['display'] = keys['display'] 1114 dup_keys = keys.dup 1115 1116 # keys for toplevel : allow followings 1117 toplevel_keys = {} 1118 ['height', 'width', 'background', 'menu'].each{|k| 1119 toplevel_keys[k] = dup_keys.delete(k) if dup_keys.key?(k) 1120 } 1121 toplevel_keys['classname'] = 'SafeTk' 1122 toplevel_keys['screen'] = dup_keys.delete('display') 1123 1124 # other keys used by pack option of container frame 1125 1126 # create toplevel widget 1127 begin 1128 top = TkToplevel.new(toplevel_keys) 1129 rescue NameError => e 1130 fail e unless @interp.safe? 1131 fail SecurityError, "unable create toplevel on the safe interpreter" 1132 end 1133 msg = "Untrusted Ruby/Tk applet (#{slave_name})" 1134 if app_name.kind_of?(String) 1135 top.title "#{app_name} (#{slave_name})" 1136 else 1137 top.title msg 1138 end 1139 1140 # procedure to delete slave interpreter 1141 slave_delete_proc = proc{ 1142 unless slave_ip.deleted? 1143 #if slave_ip._invoke('info', 'command', '.') != "" 1144 # slave_ip._invoke('destroy', '.') 1145 #end 1146 #slave_ip.delete 1147 slave_ip._eval_without_enc('exit') 1148 end 1149 begin 1150 top.destroy if top.winfo_exist? 1151 rescue 1152 # ignore 1153 end 1154 } 1155 tag = TkBindTag.new.bind('Destroy', slave_delete_proc) 1156 1157 top.bindtags = top.bindtags.unshift(tag) 1158 1159 # create control frame 1160 TkFrame.new(top, :bg=>'red', :borderwidth=>3, :relief=>'ridge') {|fc| 1161 fc.bindtags = fc.bindtags.unshift(tag) 1162 1163 TkFrame.new(fc, :bd=>0){|f| 1164 TkButton.new(f, 1165 :text=>'Delete', :bd=>1, :padx=>2, :pady=>0, 1166 :highlightthickness=>0, :command=>slave_delete_proc 1167 ).pack(:side=>:right, :fill=>:both) 1168 f.pack(:side=>:right, :fill=>:both, :expand=>true) 1169 } 1170 1171 TkLabel.new(fc, :text=>msg, :padx=>2, :pady=>0, 1172 :anchor=>:w).pack(:side=>:left, :fill=>:both, :expand=>true) 1173 1174 fc.pack(:side=>:bottom, :fill=>:x) 1175 } 1176 1177 # container frame for slave interpreter 1178 dup_keys['fill'] = :both unless dup_keys.key?('fill') 1179 dup_keys['expand'] = true unless dup_keys.key?('expand') 1180 c = TkFrame.new(top, :container=>true).pack(dup_keys) 1181 c.bind('Destroy', proc{top.destroy}) 1182 1183 # return keys 1184 loadTk_keys['use'] = TkWinfo.id(c) 1185 [loadTk_keys, top.path] 1186 end 1187 private :__create_safetk_frame 1188 1189 def __create_safe_slave_obj(safe_opts, app_name, tk_opts) 1190 raise SecurityError, "no permission to manipulate" unless self.manipulable? 1191 1192 # safe interpreter 1193 ip_name = _create_slave_ip_name 1194 slave_ip = @interp.create_slave(ip_name, true) 1195 slave_ip.instance_eval{ 1196 @force_default_encoding ||= TkUtil.untrust([false]) 1197 @encoding ||= TkUtil.untrust([nil]) 1198 def @encoding.to_s; self.join(nil); end 1199 } 1200 @slave_ip_tbl[ip_name] = slave_ip 1201 def slave_ip.safe_base? 1202 true 1203 end 1204 1205 @interp._eval("::safe::interpInit #{ip_name}") 1206 1207 slave_ip._invoke('set', 'argv0', app_name) if app_name.kind_of?(String) 1208 1209 if tk_opts 1210 tk_opts = __check_safetk_optkeys(tk_opts) 1211 if tk_opts.key?('use') 1212 @slave_ip_top[ip_name] = '' 1213 else 1214 tk_opts, top_path = __create_safetk_frame(slave_ip, ip_name, app_name, 1215 tk_opts) 1216 @slave_ip_top[ip_name] = top_path 1217 end 1218 @interp._eval("::safe::loadTk #{ip_name} #{_keys2opts(tk_opts)}") 1219 @interp._invoke('__replace_slave_tk_commands__', ip_name) 1220 else 1221 @slave_ip_top[ip_name] = nil 1222 end 1223 1224 if safe_opts.key?('deleteHook') || safe_opts.key?(:deleteHook) 1225 @interp._eval("::safe::interpConfigure #{ip_name} " + 1226 _keys2opts(safe_opts)) 1227 else 1228 @interp._eval("::safe::interpConfigure #{ip_name} " + 1229 _keys2opts(safe_opts) + '-deleteHook {' + 1230 TkComm._get_eval_string(proc{|slave| 1231 self._default_delete_hook(slave) 1232 }) + '}') 1233 end 1234 1235 [slave_ip, ip_name] 1236 end 1237 1238 def __create_trusted_slave_obj(name, keys) 1239 raise SecurityError, "no permission to manipulate" unless self.manipulable? 1240 1241 ip_name = _create_slave_ip_name 1242 slave_ip = @interp.create_slave(ip_name, false) 1243 slave_ip.instance_eval{ 1244 @force_default_encoding ||= TkUtil.untrust([false]) 1245 @encoding ||= TkUtil.untrust([nil]) 1246 def @encoding.to_s; self.join(nil); end 1247 } 1248 slave_ip._invoke('set', 'argv0', name) if name.kind_of?(String) 1249 slave_ip._invoke('set', 'argv', _keys2opts(keys)) 1250 @interp._invoke('load', '', 'Tk', ip_name) 1251 @interp._invoke('__replace_slave_tk_commands__', ip_name) 1252 @slave_ip_tbl[ip_name] = slave_ip 1253 [slave_ip, ip_name] 1254 end 1255 1256 ###################################### 1257 1258 def _create_slave_object(keys={}) 1259 raise SecurityError, "no permission to manipulate" unless self.manipulable? 1260 1261 ip = MultiTkIp.new_slave(self, keys={}) 1262 @slave_ip_tbl[ip.name] = ip 1263 end 1264 1265 ###################################### 1266 1267 def initialize(master, safeip=true, keys={}) 1268 if $SAFE >= 4 1269 fail SecurityError, "cannot create a new interpreter at level #{$SAFE}" 1270 end 1271 1272 if safeip == nil && $SAFE >= 2 1273 fail SecurityError, "cannot create a master-ip at level #{$SAFE}" 1274 end 1275 1276 if master.deleted? && safeip == nil 1277 fail RuntimeError, "cannot create a slave of a deleted interpreter" 1278 end 1279 1280 if !master.deleted? && !master.master? && master.safe? 1281 fail SecurityError, "safe-slave-ip cannot create a new interpreter" 1282 end 1283 1284 if safeip == nil && !master.master? 1285 fail SecurityError, "slave-ip cannot create a master-ip" 1286 end 1287 1288 unless keys.kind_of? Hash 1289 fail ArgumentError, "expecting a Hash object for the 2nd argument" 1290 end 1291 1292 @tk_windows = {} 1293 @tk_table_list = [] 1294 @slave_ip_tbl = {} 1295 @slave_ip_top = {} 1296 @cb_error_proc = [] 1297 @evloop_thread = [] 1298 1299 TkUtil.untrust(@tk_windows) unless @tk_windows.tainted? 1300 TkUtil.untrust(@tk_table_list) unless @tk_table_list.tainted? 1301 TkUtil.untrust(@slave_ip_tbl) unless @slave_ip_tbl.tainted? 1302 TkUtil.untrust(@slave_ip_top) unless @slave_ip_top.tainted? 1303 TkUtil.untrust(@cb_error_proc) unless @cb_error_proc.tainted? 1304 TkUtil.untrust(@evloop_thread) unless @evloop_thread.tainted? 1305 1306 @callback_status = [] 1307 1308 name, safe, safe_opts, tk_opts = _parse_slaveopts(keys) 1309 1310 safe = 4 if safe && !safe.kind_of?(Fixnum) 1311 1312 @safe_base = false 1313 1314 if safeip == nil 1315 # create master-ip 1316 unless WITH_RUBY_VM 1317 @interp = TclTkIp.new(name, _keys2opts(tk_opts)) 1318 @interp.instance_eval{ 1319 @force_default_encoding ||= TkUtil.untrust([false]) 1320 @encoding ||= TkUtil.untrust([nil]) 1321 def @encoding.to_s; self.join(nil); end 1322 } 1323 1324 else ### Ruby 1.9 !!!!!!!!!!! 1325=begin 1326 @interp_thread = Thread.new{ 1327 Thread.current[:interp] = interp = TclTkIp.new(name, _keys2opts(tk_opts)) 1328 interp.instance_eval{ 1329 @force_default_encoding ||= TkUtil.untrust([false]) 1330 @encoding ||= TkUtil.untrust([nil]) 1331 def @encoding.to_s; self.join(nil); end 1332 } 1333 1334 #sleep 1335 TclTkLib.mainloop(true) 1336 } 1337 until @interp_thread[:interp] 1338 Thread.pass 1339 end 1340 # INTERP_THREAD.run 1341 @interp = @interp_thread[:interp] 1342=end 1343 @interp_thread = Thread.new{ 1344 current = Thread.current 1345 begin 1346 current[:interp] = interp = TclTkIp.new(name, _keys2opts(tk_opts)) 1347 rescue e 1348 current[:interp] = e 1349 raise e 1350 end 1351 #sleep 1352 #TclTkLib.mainloop(true) 1353 current[:mutex] = mutex = Mutex.new 1354 current[:root_check] = cond_ver = ConditionVariable.new 1355 1356 status = [nil] 1357 def status.value 1358 self[0] 1359 end 1360 def status.value=(val) 1361 self[0] = val 1362 end 1363 current[:status] = status 1364 1365 begin 1366 begin 1367 current[:status].value = interp.mainloop(true) 1368 rescue SystemExit=>e 1369 current[:status].value = e 1370 rescue Exception=>e 1371 current[:status].value = e 1372 retry if interp.has_mainwindow? 1373 ensure 1374 mutex.synchronize{ cond_var.broadcast } 1375 end 1376 current[:status].value = interp.mainloop(false) 1377 ensure 1378 interp.delete 1379 end 1380 } 1381 until @interp_thread[:interp] 1382 Thread.pass 1383 end 1384 # INTERP_THREAD.run 1385 @interp = @interp_thread[:interp] 1386 1387 @evloop_thread[0] = @interp_thread 1388 1389 def self.mainloop(check_root = true) 1390 begin 1391 TclTkLib.set_eventloop_window_mode(true) 1392 @interp_thread.value 1393 ensure 1394 TclTkLib.set_eventloop_window_mode(false) 1395 end 1396 end 1397 end 1398 1399 @interp.instance_eval{ 1400 @force_default_encoding ||= TkUtil.untrust([false]) 1401 @encoding ||= TkUtil.untrust([nil]) 1402 def @encoding.to_s; self.join(nil); end 1403 } 1404 1405 @ip_name = nil 1406 1407 if safe 1408 safe = $SAFE if safe < $SAFE 1409 @safe_level = [safe] 1410 else 1411 @safe_level = [$SAFE] 1412 end 1413 1414 else 1415 # create slave-ip 1416 if safeip || master.safe? 1417 @safe_base = true 1418 @interp, @ip_name = master.__create_safe_slave_obj(safe_opts, 1419 name, tk_opts) 1420 # @interp_thread = nil if RUBY_VERSION < '1.9.0' ### !!!!!!!!!!! 1421 @interp_thread = nil unless WITH_RUBY_VM ### Ruby 1.9 !!!!!!!!!!! 1422 if safe 1423 safe = master.safe_level if safe < master.safe_level 1424 @safe_level = [safe] 1425 else 1426 @safe_level = [4] 1427 end 1428 else 1429 @interp, @ip_name = master.__create_trusted_slave_obj(name, tk_opts) 1430 # @interp_thread = nil if RUBY_VERSION < '1.9.0' ### !!!!!!!!!!! 1431 @interp_thread = nil unless WITH_RUBY_VM ### Ruby 1.9 !!!!!!!!!!! 1432 if safe 1433 safe = master.safe_level if safe < master.safe_level 1434 @safe_level = [safe] 1435 else 1436 @safe_level = [master.safe_level] 1437 end 1438 end 1439 @set_alias_proc = proc{|name| 1440 master._invoke('interp', 'alias', @ip_name, name, '', name) 1441 }.freeze 1442 end 1443 1444 @system = Object.new 1445 1446 @wait_on_mainloop = TkUtil.untrust([true, 0]) 1447 # @wait_on_mainloop = TkUtil.untrust([false, 0]) 1448 1449 @threadgroup = ThreadGroup.new 1450 1451 @pseudo_toplevel = [false, nil] 1452 1453 @cmd_queue = MultiTkIp::Command_Queue.new(@interp) 1454 1455=begin 1456 @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog(@safe_level[0]) 1457 1458 @threadgroup.add @cmd_receiver 1459 @threadgroup.add @receiver_watchdog 1460 1461 @threadgroup.enclose 1462=end 1463 @@DEFAULT_MASTER.assign_receiver_and_watchdog(self) 1464 1465 @@IP_TABLE[@threadgroup] = self 1466 @@TK_TABLE_LIST.size.times{ 1467 @tk_table_list << TkUtil.untrust({}) 1468 } 1469 _init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS) 1470 1471 class << self 1472 undef :instance_eval 1473 end 1474 1475 # dummy call for initialization 1476 self.eval_proc{ Tk.tk_call('set', 'tcl_patchLevel') } 1477 1478 self.freeze # defend against modification 1479 end 1480 1481 ###################################### 1482 1483 def _default_delete_hook(slave) 1484 raise SecurityError, "no permission to manipulate" unless self.manipulable? 1485 @slave_ip_tbl.delete(slave) 1486 top = @slave_ip_top.delete(slave) 1487 if top.kind_of?(String) 1488 # call default hook of safetk.tcl (ignore exceptions) 1489 if top == '' 1490 begin 1491 @interp._eval("::safe::disallowTk #{slave}") 1492 rescue 1493 warn("Waring: fail to call '::safe::disallowTk'") if $DEBUG 1494 end 1495 else # toplevel path 1496 begin 1497 @interp._eval("::safe::tkDelete {} #{top} #{slave}") 1498 rescue 1499 warn("Waring: fail to call '::safe::tkDelete'") if $DEBUG 1500 begin 1501 @interp._eval("destroy #{top}") 1502 rescue 1503 warn("Waring: fail to destroy toplevel") if $DEBUG 1504 end 1505 end 1506 end 1507 end 1508 end 1509 1510end 1511 1512 1513# get target IP 1514class MultiTkIp 1515 @@CALLBACK_SUBTHREAD = Class.new(Thread){ 1516 def self.new(interp, &blk) 1517 super(interp){|ip| Thread.current[:callback_ip] = ip; blk.call} 1518 end 1519 1520 @table = TkUtil.untrust(Hash.new{|h,k| h[k] = TkUtil.untrust([])}) 1521 def self.table 1522 @table 1523 end 1524 } 1525 1526 def self._ip_id_ 1527 __getip._ip_id_ 1528 end 1529 def _ip_id_ 1530 # for RemoteTkIp 1531 '' 1532 end 1533 1534 def self.__getip 1535 current = Thread.current 1536 if current.kind_of?(@@CALLBACK_SUBTHREAD) 1537 return current[:callback_ip] 1538 end 1539 if TclTkLib.mainloop_thread? != false && current[:callback_ip] 1540 return current[:callback_ip] 1541 end 1542 if current.group == ThreadGroup::Default 1543 @@DEFAULT_MASTER 1544 else 1545 ip = @@IP_TABLE[current.group] 1546 unless ip 1547 fail SecurityError, 1548 "cannot call Tk methods on #{Thread.current.inspect}" 1549 end 1550 ip 1551 end 1552 end 1553end 1554 1555 1556# aliases of constructor 1557class << MultiTkIp 1558 alias __new new 1559 private :__new 1560 1561 def new_master(safe=nil, keys={}, &blk) 1562 if MultiTkIp::WITH_RUBY_VM 1563 #### TODO !!!!!! 1564 fail RuntimeError, 1565 'sorry, still not support multiple master-interpreters on RubyVM' 1566 end 1567 1568 if safe.kind_of?(Hash) 1569 keys = safe 1570 elsif safe.kind_of?(Integer) 1571 raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash) 1572 if !keys.key?(:safe) && !keys.key?('safe') 1573 keys[:safe] = safe 1574 end 1575 elsif safe == nil 1576 # do nothing 1577 else 1578 raise ArgumentError, "unexpected argument(s)" 1579 end 1580 1581 ip = __new(__getip, nil, keys) 1582 #ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? 1583 if block_given? 1584 #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} 1585 #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; yield}.call)} 1586 ip._proc_on_safelevel(&blk).call(ip.safe_level) 1587 end 1588 ip 1589 end 1590 1591 alias new new_master 1592 1593 def new_slave(safe=nil, keys={}, &blk) 1594 if safe.kind_of?(Hash) 1595 keys = safe 1596 elsif safe.kind_of?(Integer) 1597 raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash) 1598 if !keys.key?(:safe) && !keys.key?('safe') 1599 keys[:safe] = safe 1600 end 1601 elsif safe == nil 1602 # do nothing 1603 else 1604 raise ArgumentError, "unexpected argument(s)" 1605 end 1606 1607 ip = __new(__getip, false, keys) 1608 # ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? 1609 if block_given? 1610 #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} 1611 #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; yield}.call)} 1612 ip._proc_on_safelevel(&blk).call(ip.safe_level) 1613 end 1614 ip 1615 end 1616 alias new_trusted_slave new_slave 1617 1618 def new_safe_slave(safe=4, keys={}, &blk) 1619 if safe.kind_of?(Hash) 1620 keys = safe 1621 elsif safe.kind_of?(Integer) 1622 raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash) 1623 if !keys.key?(:safe) && !keys.key?('safe') 1624 keys[:safe] = safe 1625 end 1626 else 1627 raise ArgumentError, "unexpected argument(s)" 1628 end 1629 1630 ip = __new(__getip, true, keys) 1631 # ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? 1632 if block_given? 1633 #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} 1634 #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; yield}.call)} 1635 ip._proc_on_safelevel(&blk).call(ip.safe_level) 1636 end 1637 ip 1638 end 1639 alias new_safeTk new_safe_slave 1640end 1641 1642 1643# get info 1644class MultiTkIp 1645 def inspect 1646 s = self.to_s.chop! 1647 if self.manipulable? 1648 if master? 1649 if @interp.deleted? 1650 s << ':deleted-master' 1651 else 1652 s << ':master' 1653 end 1654 else 1655 if @interp.deleted? 1656 s << ':deleted-slave' 1657 elsif @interp.safe? 1658 s << ':safe-slave' 1659 else 1660 s << ':trusted-slave' 1661 end 1662 end 1663 end 1664 s << '>' 1665 end 1666 1667 def master? 1668 if @ip_name 1669 false 1670 else 1671 true 1672 end 1673 end 1674 def self.master? 1675 __getip.master? 1676 end 1677 1678 def slave? 1679 not master? 1680 end 1681 def self.slave? 1682 not self.master? 1683 end 1684 1685 def alive? 1686 raise SecurityError, "no permission to manipulate" unless self.manipulable? 1687 begin 1688 return false unless @cmd_receiver.alive? 1689 return false if @interp.deleted? 1690 return false if @interp._invoke('interp', 'exists', '') == '0' 1691 rescue Exception 1692 return false 1693 end 1694 true 1695 end 1696 def self.alive? 1697 __getip.alive? 1698 end 1699 1700 def path 1701 @ip_name || '' 1702 end 1703 def self.path 1704 __getip.path 1705 end 1706 def ip_name 1707 @ip_name || '' 1708 end 1709 def self.ip_name 1710 __getip.ip_name 1711 end 1712 def to_eval 1713 @ip_name || '' 1714 end 1715 def self.to_eval 1716 __getip.to_eval 1717 end 1718 1719 def slaves(all = false) 1720 raise SecurityError, "no permission to manipulate" unless self.manipulable? 1721 @interp._invoke('interp','slaves').split.map!{|name| 1722 if @slave_ip_tbl.key?(name) 1723 @slave_ip_tbl[name] 1724 elsif all 1725 name 1726 else 1727 nil 1728 end 1729 }.compact! 1730 end 1731 def self.slaves(all = false) 1732 __getip.slaves(all) 1733 end 1734 1735 def manipulable? 1736 return true if (Thread.current.group == ThreadGroup::Default) 1737 ip = MultiTkIp.__getip 1738 (ip == self) || ip._is_master_of?(@interp) 1739 end 1740 def self.manipulable? 1741 true 1742 end 1743 1744 def _is_master_of?(tcltkip_obj) 1745 tcltkip_obj.slave_of?(@interp) 1746 end 1747 protected :_is_master_of? 1748end 1749 1750 1751# instance methods to treat tables 1752class MultiTkIp 1753 def _tk_cmd_tbl 1754 tbl = {} 1755 MultiTkIp.tk_cmd_tbl.each{|id, ent| tbl[id] = ent if ent.ip == self } 1756 tbl 1757 end 1758 1759 def _tk_windows 1760 @tk_windows 1761 end 1762 1763 def _tk_table_list 1764 @tk_table_list 1765 end 1766 1767 def _add_new_tables 1768 (@@TK_TABLE_LIST.size - @tk_table_list.size).times{ 1769 @tk_table_list << TkUtil.untrust({}) 1770 } 1771 end 1772 1773 def _init_ip_env(script) 1774 self.eval_proc{script.call(self)} 1775 end 1776 1777 def _add_tk_procs(name, args, body) 1778 return if slave? 1779 @interp._invoke('proc', name, args, body) if args && body 1780 @interp._invoke('interp', 'slaves').split.each{|slave| 1781 @interp._invoke('interp', 'alias', slave, name, '', name) 1782 } 1783 end 1784 1785 def _remove_tk_procs(*names) 1786 return if slave? 1787 names.each{|name| 1788 name = name.to_s 1789 1790 return if @interp.deleted? 1791 @interp._invoke('rename', name, '') 1792 1793 return if @interp.deleted? 1794 @interp._invoke('interp', 'slaves').split.each{|slave| 1795 return if @interp.deleted? 1796 @interp._invoke('interp', 'alias', slave, name, '') rescue nil 1797 } 1798 } 1799 end 1800 1801 def _init_ip_internal(init_ip_env, add_tk_procs) 1802 #init_ip_env.each{|script| self.eval_proc{script.call(self)}} 1803 init_ip_env.each{|script| self._init_ip_env(script)} 1804 add_tk_procs.each{|name, args, body| 1805 if master? 1806 @interp._invoke('proc', name, args, body) if args && body 1807 else 1808 @set_alias_proc.call(name) 1809 end 1810 } 1811 end 1812end 1813 1814 1815# class methods to treat tables 1816class MultiTkIp 1817 def self.tk_cmd_tbl 1818 @@TK_CMD_TBL 1819 end 1820 def self.tk_windows 1821 __getip._tk_windows 1822 end 1823 def self.tk_object_table(id) 1824 __getip._tk_table_list[id] 1825 end 1826 def self.create_table 1827 if __getip.slave? 1828 begin 1829 raise SecurityError, "slave-IP has no permission creating a new table" 1830 rescue SecurityError => e 1831 #p e.backtrace 1832 # Is called on a Ruby/Tk library? 1833 caller_info = e.backtrace[1] 1834 if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:} 1835 # Probably, caller is a Ruby/Tk library --> allow creating 1836 else 1837 raise e 1838 end 1839 end 1840 end 1841 1842 id = @@TK_TABLE_LIST.size 1843 obj = Object.new 1844 @@TK_TABLE_LIST << obj 1845 obj.instance_variable_set(:@id, id) 1846 obj.instance_variable_set(:@mutex, Mutex.new) 1847 obj.instance_eval{ 1848 def self.mutex 1849 @mutex 1850 end 1851 def self.method_missing(m, *args) 1852 MultiTkIp.tk_object_table(@id).__send__(m, *args) 1853 end 1854 } 1855 obj.freeze 1856 @@IP_TABLE.each{|tg, ip| ip._add_new_tables } 1857 return obj 1858 end 1859 1860 def self.init_ip_env(script = Proc.new) 1861 @@INIT_IP_ENV << script 1862 if __getip.slave? 1863 begin 1864 raise SecurityError, "slave-IP has no permission initializing IP env" 1865 rescue SecurityError => e 1866 #p e.backtrace 1867 # Is called on a Ruby/Tk library? 1868 caller_info = e.backtrace[1] 1869 if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:} 1870 # Probably, caller is a Ruby/Tk library --> allow creating 1871 else 1872 raise e 1873 end 1874 end 1875 end 1876 1877 # @@IP_TABLE.each{|tg, ip| 1878 # ip._init_ip_env(script) 1879 # } 1880 @@DEFAULT_MASTER.__init_ip_env__(@@IP_TABLE, script) 1881 end 1882 1883 def self.add_tk_procs(name, args=nil, body=nil) 1884 if name.kind_of?(Array) # => an array of [name, args, body] 1885 name.each{|param| self.add_tk_procs(*param)} 1886 else 1887 name = name.to_s 1888 @@ADD_TK_PROCS << [name, args, body] 1889 @@IP_TABLE.each{|tg, ip| 1890 ip._add_tk_procs(name, args, body) 1891 } 1892 end 1893 end 1894 1895 def self.remove_tk_procs(*names) 1896 names.each{|name| 1897 name = name.to_s 1898 @@ADD_TK_PROCS.delete_if{|elem| 1899 elem.kind_of?(Array) && elem[0].to_s == name 1900 } 1901 } 1902 @@IP_TABLE.each{|tg, ip| 1903 ip._remove_tk_procs(*names) 1904 } 1905 end 1906 1907 def self.init_ip_internal 1908 __getip._init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS) 1909 end 1910end 1911 1912# for callback operation 1913class MultiTkIp 1914 def self.cb_entry_class 1915 @@CB_ENTRY_CLASS 1916 end 1917 def self.get_cb_entry(cmd) 1918 @@CB_ENTRY_CLASS.new(__getip, cmd).freeze 1919 end 1920 1921=begin 1922 def cb_eval(cmd, *args) 1923 #self.eval_callback{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) } 1924 #ret = self.eval_callback{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) } 1925 ret = self.eval_callback(*args){|safe, *params| 1926 $SAFE=safe if $SAFE < safe 1927 TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) 1928 } 1929 if ret.kind_of?(Exception) 1930 raise ret 1931 end 1932 ret 1933 end 1934=end 1935 def cb_eval(cmd, *args) 1936 self.eval_callback(*args, 1937 &_proc_on_safelevel{|*params| 1938 TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) 1939 }) 1940 end 1941=begin 1942 def cb_eval(cmd, *args) 1943 self.eval_callback(*args){|safe, *params| 1944 $SAFE=safe if $SAFE < safe 1945 # TkUtil.eval_cmd(cmd, *params) 1946 TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) 1947 } 1948 end 1949=end 1950=begin 1951 def cb_eval(cmd, *args) 1952 @callback_status[0] ||= TkVariable.new 1953 @callback_status[1] ||= TkVariable.new 1954 st, val = @callback_status 1955 th = Thread.new{ 1956 self.eval_callback(*args){|safe, *params| 1957 #p [status, val, safe, *params] 1958 $SAFE=safe if $SAFE < safe 1959 begin 1960 TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) 1961 rescue TkCallbackContinue 1962 st.value = 4 1963 rescue TkCallbackBreak 1964 st.value = 3 1965 rescue TkCallbackReturn 1966 st.value = 2 1967 rescue Exception => e 1968 val.value = e.message 1969 st.value = 1 1970 else 1971 st.value = 0 1972 end 1973 } 1974 } 1975 begin 1976 st.wait 1977 status = st.numeric 1978 retval = val.value 1979 rescue => e 1980 fail e 1981 end 1982 1983 if status == 1 1984 fail RuntimeError, retval 1985 elsif status == 2 1986 fail TkCallbackReturn, "Tk callback returns 'return' status" 1987 elsif status == 3 1988 fail TkCallbackBreak, "Tk callback returns 'break' status" 1989 elsif status == 4 1990 fail TkCallbackContinue, "Tk callback returns 'continue' status" 1991 else 1992 '' 1993 end 1994 end 1995=end 1996 1997end 1998 1999# pseudo-toplevel operation support 2000class MultiTkIp 2001 # instance method 2002 def __pseudo_toplevel 2003 ip = MultiTkIp.__getip 2004 (ip == @@DEFAULT_MASTER || ip == self) && 2005 self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1] 2006 end 2007 2008 def __pseudo_toplevel=(m) 2009 unless (Thread.current.group == ThreadGroup::Default && 2010 MultiTkIp.__getip == @@DEFAULT_MASTER) 2011 fail SecurityError, "no permission to manipulate" 2012 end 2013 2014 # if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?) 2015 if m.respond_to?(:pseudo_toplevel_evaluable?) 2016 @pseudo_toplevel[0] = true 2017 @pseudo_toplevel[1] = m 2018 else 2019 fail ArgumentError, 'fail to set pseudo-toplevel' 2020 end 2021 self 2022 end 2023 2024 def __pseudo_toplevel_evaluable? 2025 begin 2026 @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable? 2027 rescue Exception 2028 false 2029 end 2030 end 2031 2032 def __pseudo_toplevel_evaluable=(mode) 2033 unless (Thread.current.group == ThreadGroup::Default && 2034 MultiTkIp.__getip == @@DEFAULT_MASTER) 2035 fail SecurityError, "no permission to manipulate" 2036 end 2037 2038 @pseudo_toplevel[0] = (mode)? true: false 2039 end 2040end 2041 2042 2043################################################ 2044# use pseudo-toplevel feature of MultiTkIp ? 2045if (!defined?(Use_PseudoToplevel_Feature_of_MultiTkIp) || 2046 Use_PseudoToplevel_Feature_of_MultiTkIp) 2047 module MultiTkIp_PseudoToplevel_Evaluable 2048 #def pseudo_toplevel_eval(body = Proc.new) 2049 # Thread.current[:TOPLEVEL] = self 2050 # begin 2051 # body.call 2052 # ensure 2053 # Thread.current[:TOPLEVEL] = nil 2054 # end 2055 #end 2056 2057 def pseudo_toplevel_evaluable? 2058 @pseudo_toplevel_evaluable 2059 end 2060 2061 def pseudo_toplevel_evaluable=(mode) 2062 @pseudo_toplevel_evaluable = (mode)? true: false 2063 end 2064 2065 def self.extended(mod) 2066 mod.__send__(:extend_object, mod) 2067 mod.instance_variable_set('@pseudo_toplevel_evaluable', true) 2068 end 2069 end 2070 2071 class Object 2072 alias __method_missing_alias_for_MultiTkIp__ method_missing 2073 private :__method_missing_alias_for_MultiTkIp__ 2074 2075 def method_missing(id, *args) 2076 begin 2077 has_top = (top = MultiTkIp.__getip.__pseudo_toplevel) && 2078 top.respond_to?(:pseudo_toplevel_evaluable?) && 2079 top.pseudo_toplevel_evaluable? && 2080 top.respond_to?(id) 2081 rescue Exception => e 2082 has_top = false 2083 end 2084 2085 if has_top 2086 top.__send__(id, *args) 2087 else 2088 __method_missing_alias_for_MultiTkIp__(id, *args) 2089 end 2090 end 2091 end 2092else 2093 # dummy 2094 module MultiTkIp_PseudoToplevel_Evaluable 2095 def pseudo_toplevel_evaluable? 2096 false 2097 end 2098 end 2099end 2100 2101 2102################################################ 2103# evaluate a procedure on the proper interpreter 2104class MultiTkIp 2105 # instance & class method 2106 def _proc_on_safelevel(cmd=nil) # require a block for eval 2107 if cmd 2108 if cmd.kind_of?(Method) 2109 _proc_on_safelevel{|*args| cmd.call(*args)} 2110 else 2111 _proc_on_safelevel(&cmd) 2112 end 2113 else 2114 #Proc.new{|safe, *args| $SAFE=safe if $SAFE < safe; yield(*args)} 2115 Proc.new{|safe, *args| 2116 # avoid security error on Exception objects 2117 untrust_proc = proc{|err| 2118 begin 2119 err.untrust if err.respond_to?(:untrust) 2120 rescue SecurityError 2121 end 2122 err 2123 } 2124 $SAFE=safe if $SAFE < safe; 2125 begin 2126 yield(*args) 2127 rescue Exception => e 2128 fail untrust_proc.call(e) 2129 end 2130 } 2131 end 2132 end 2133 def MultiTkIp._proc_on_safelevel(cmd=nil, &blk) 2134 MultiTkIp.__getip._proc_on_safelevel(cmd, &blk) 2135 end 2136 2137 def _proc_on_current_safelevel(cmd=nil, &blk) # require a block for eval 2138 safe = $SAFE 2139 cmd = _proc_on_safelevel(cmd, &blk) 2140 Proc.new{|*args| cmd.call(safe, *args)} 2141 end 2142 def MultiTkIp._proc_on_current_safelevel(cmd=nil, &blk) 2143 MultiTkIp.__getip._proc_on_current_safelevel(cmd, &blk) 2144 end 2145 2146 ###################################### 2147 # instance method 2148 def eval_proc_core(req_val, cmd, *args) 2149 # check 2150 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2151 unless cmd.kind_of?(Proc) || cmd.kind_of?(Method) 2152 raise RuntimeError, "A Proc/Method object is expected for the 'cmd' argument" 2153 end 2154 2155 # on IP thread 2156 if @cmd_receiver == Thread.current || 2157 (!req_val && TclTkLib.mainloop_thread? != false) # callback 2158 begin 2159 ret = cmd.call(safe_level, *args) 2160 rescue SystemExit => e 2161 # exit IP 2162 warn("Warning: "+ e.inspect + " on " + self.inspect) if $DEBUG 2163 begin 2164 self._eval_without_enc('exit') 2165 rescue Exception => e 2166 end 2167 self.delete 2168 ret = nil 2169 rescue Exception => e 2170 if $DEBUG 2171 warn("Warning: " + e.class.inspect + 2172 ((e.message.length > 0)? ' "' + e.message + '"': '') + 2173 " on " + self.inspect) 2174 end 2175=begin 2176 begin 2177 bt = _toUTF8(e.backtrace.join("\n")) 2178 bt.instance_variable_set(:@encoding, 'utf-8') 2179 rescue Exception 2180 bt = e.backtrace.join("\n") 2181 end 2182 begin 2183 @interp._set_global_var('errorInfo', bt) 2184 rescue Exception 2185 end 2186=end 2187 ret = e 2188 end 2189 return ret 2190 end 2191 2192 # send cmd to the proc-queue 2193 unless req_val 2194 begin 2195 @cmd_queue.enq([nil, cmd, *args]) 2196 rescue Exception => e 2197 # ignore 2198 if $DEBUG 2199 warn("Warning: " + e.class.inspect + 2200 ((e.message.length > 0)? ' "' + e.message + '"': '') + 2201 " on " + self.inspect) 2202 end 2203 return e 2204 end 2205 return nil 2206 end 2207 2208 # send and get return value by exception 2209 begin 2210 @cmd_queue.enq([Thread.current, cmd, *args]) 2211 Thread.stop 2212 rescue MultiTkIp_OK => ret 2213 # return value 2214 return ret.value 2215 rescue SystemExit => e 2216 # exit IP 2217 warn("Warning: " + e.inspect + " on " + self.inspect) if $DEBUG 2218 begin 2219 self._eval_without_enc('exit') 2220 rescue Exception 2221 end 2222 if !self.deleted? && !safe? && allow_ruby_exit? 2223 self.delete 2224 fail e 2225 else 2226 self.delete 2227 end 2228 rescue Exception => e 2229 if $DEBUG 2230 warn("Warning: " + e.class.inspect + 2231 ((e.message.length > 0)? ' "' + e.message + '"': '') + 2232 " on " + self.inspect) 2233 end 2234 return e 2235 end 2236 return nil 2237 end 2238 private :eval_proc_core 2239 2240if false && WITH_RUBY_VM ### Ruby 1.9 2241 # Not stable, so disable this feature 2242 def eval_callback(*args) 2243 if block_given? 2244 cmd = Proc.new 2245 else 2246 cmd = args.shift 2247 end 2248 begin 2249 if @@CALLBACK_SUBTHREAD.table[self].index(Thread.current) 2250 last_th = nil 2251 else 2252 last_th = @@CALLBACK_SUBTHREAD.table[self][-1] 2253 end 2254 @@CALLBACK_SUBTHREAD.new(self){ 2255 @@CALLBACK_SUBTHREAD.table[self] << Thread.current 2256 begin 2257 last_th.join if last_th 2258 eval_proc_core(false, cmd, *args) 2259 rescue Exception=>e 2260 e 2261 ensure 2262 @@CALLBACK_SUBTHREAD.table[self].delete(Thread.current) 2263 end 2264 } 2265 end 2266 end 2267else ### Ruby 1.8 2268 def eval_callback(*args) 2269 if block_given? 2270 cmd = Proc.new 2271 else 2272 cmd = args.shift 2273 end 2274 begin 2275 eval_proc_core(false, cmd, *args) 2276 rescue Exception=>e 2277 e 2278 ensure 2279 end 2280 end 2281end 2282 2283 def eval_proc(*args, &blk) 2284 if block_given? 2285 cmd = _proc_on_safelevel(&blk) 2286 else 2287 unless (cmd = args.shift) 2288 fail ArgumentError, "A Proc or Method object is expected for 1st argument" 2289 end 2290 cmd = _proc_on_safelevel(&cmd) 2291 end 2292 if TclTkLib.mainloop_thread? == true 2293 # call from eventloop 2294 current = Thread.current 2295 backup_ip = current[:callback_ip] 2296 current[:callback_ip] = self 2297 begin 2298 eval_proc_core(false, cmd, *args) 2299 ensure 2300 current[:callback_ip] = backup_ip 2301 end 2302 else 2303 eval_proc_core(true, 2304 proc{|safe, *params| 2305 Thread.new{cmd.call(safe, *params)}.value 2306 }, 2307 *args) 2308 end 2309 end 2310=begin 2311 def eval_proc(*args) 2312 # The scope of the eval-block of 'eval_proc' method is different from 2313 # the external. If you want to pass local values to the eval-block, 2314 # use arguments of eval_proc method. They are passed to block-arguments. 2315 if block_given? 2316 cmd = Proc.new 2317 else 2318 unless (cmd = args.shift) 2319 fail ArgumentError, "A Proc or Method object is expected for 1st argument" 2320 end 2321 end 2322 if TclTkLib.mainloop_thread? == true 2323 # call from eventloop 2324 current = Thread.current 2325 backup_ip = current[:callback_ip] 2326 current[:callback_ip] = self 2327 begin 2328 eval_proc_core(false, 2329 proc{|safe, *params| 2330 $SAFE=safe if $SAFE < safe 2331 cmd.call(*params) 2332 }, *args) 2333 ensure 2334 current[:callback_ip] = backup_ip 2335 end 2336 else 2337 eval_proc_core(true, 2338 proc{|safe, *params| 2339 $SAFE=safe if $SAFE < safe 2340 Thread.new(*params, &cmd).value 2341 }, 2342 *args) 2343 end 2344 end 2345=end 2346 alias call eval_proc 2347 2348 def bg_eval_proc(*args) 2349 if block_given? 2350 cmd = Proc.new 2351 else 2352 unless (cmd = args.shift) 2353 fail ArgumentError, "A Proc or Method object is expected for 1st argument" 2354 end 2355 end 2356 Thread.new{ 2357 eval_proc(cmd, *args) 2358=begin 2359 eval_proc_core(false, 2360 proc{|safe, *params| 2361 $SAFE=safe if $SAFE < safe 2362 Thread.new(*params, &cmd).value 2363 }, 2364 safe_level, *args) 2365=end 2366 } 2367 end 2368 alias background_eval_proc bg_eval_proc 2369 alias thread_eval_proc bg_eval_proc 2370 alias bg_call bg_eval_proc 2371 alias background_call bg_eval_proc 2372 2373 def eval_string(cmd, *eval_args) 2374 # cmd string ==> proc 2375 unless cmd.kind_of?(String) 2376 raise RuntimeError, "A String object is expected for the 'cmd' argument" 2377 end 2378 2379 eval_proc_core(true, 2380 proc{|safe| 2381 Kernel.eval("$SAFE=#{safe} if $SAFE < #{safe};" << cmd, 2382 *eval_args) 2383 }) 2384 end 2385 alias eval_str eval_string 2386 2387 def bg_eval_string(cmd, *eval_args) 2388 # cmd string ==> proc 2389 unless cmd.kind_of?(String) 2390 raise RuntimeError, "A String object is expected for the 'cmd' argument" 2391 end 2392 Thread.new{ 2393 eval_proc_core(true, 2394 proc{|safe| 2395 Kernel.eval("$SAFE=#{safe} if $SAFE < #{safe};" << cmd, 2396 *eval_args) 2397 }) 2398 } 2399 end 2400 alias background_eval_string bg_eval_string 2401 alias bg_eval_str bg_eval_string 2402 alias background_eval_str bg_eval_string 2403 2404 def eval(*args, &blk) 2405 if block_given? 2406 eval_proc(*args, &blk) 2407 elsif args[0] 2408 if args[0].respond_to?(:call) 2409 eval_proc(*args) 2410 else 2411 eval_string(*args) 2412 end 2413 else 2414 fail ArgumentError, "no argument to eval" 2415 end 2416 end 2417 2418 def bg_eval(*args, &blk) 2419 if block_given? 2420 bg_eval_proc(*args, &blk) 2421 elsif args[0] 2422 if args[0].respond_to?(:call) 2423 bg_eval_proc(*args) 2424 else 2425 bg_eval_string(*args) 2426 end 2427 else 2428 fail ArgumentError, "no argument to eval" 2429 end 2430 end 2431 alias background_eval bg_eval 2432end 2433 2434class << MultiTkIp 2435 # class method 2436 def eval_proc(*args, &blk) 2437 # class ==> interp object 2438 __getip.eval_proc(*args, &blk) 2439 end 2440 alias call eval_proc 2441 2442 def bg_eval_proc(*args, &blk) 2443 # class ==> interp object 2444 __getip.bg_eval_proc(*args, &blk) 2445 end 2446 alias background_eval_proc bg_eval_proc 2447 alias thread_eval_proc bg_eval_proc 2448 alias bg_call bg_eval_proc 2449 alias background_call bg_eval_proc 2450 2451 def eval_string(cmd, *eval_args) 2452 # class ==> interp object 2453 __getip.eval_string(cmd, *eval_args) 2454 end 2455 alias eval_str eval_string 2456 2457 def bg_eval_string(cmd, *eval_args) 2458 # class ==> interp object 2459 __getip.bg_eval_string(cmd, *eval_args) 2460 end 2461 alias background_eval_string bg_eval_string 2462 alias bg_eval_str bg_eval_string 2463 alias background_eval_str bg_eval_string 2464 2465 def eval(*args, &blk) 2466 # class ==> interp object 2467 __getip.eval(*args, &blk) 2468 end 2469 def bg_eval(*args, &blk) 2470 # class ==> interp object 2471 __getip.bg_eval(*args, &blk) 2472 end 2473 alias background_eval bg_eval 2474end 2475 2476 2477# event loop 2478# all master/slave IPs are controled by only one event-loop 2479class MultiTkIp 2480 def self.default_master? 2481 __getip == @@DEFAULT_MASTER 2482 end 2483end 2484class << MultiTkIp 2485 def mainloop(check_root = true) 2486 __getip.mainloop(check_root) 2487 end 2488 def mainloop_watchdog(check_root = true) 2489 __getip.mainloop_watchdog(check_root) 2490 end 2491 def do_one_event(flag = TclTkLib::EventFlag::ALL) 2492 __getip.do_one_event(flag) 2493 end 2494 def mainloop_abort_on_exception 2495 # __getip.mainloop_abort_on_exception 2496 TclTkLib.mainloop_abort_on_exception 2497 end 2498 def mainloop_abort_on_exception=(mode) 2499 # __getip.mainloop_abort_on_exception=(mode) 2500 TclTkLib.mainloop_abort_on_exception=(mode) 2501 end 2502 def set_eventloop_tick(tick) 2503 __getip.set_eventloop_tick(tick) 2504 end 2505 def get_eventloop_tick 2506 __getip.get_eventloop_tick 2507 end 2508 def set_no_event_wait(tick) 2509 __getip.set_no_event_wait(tick) 2510 end 2511 def get_no_event_wait 2512 __getip.get_no_event_wait 2513 end 2514 def set_eventloop_weight(loop_max, no_event_tick) 2515 __getip.set_eventloop_weight(loop_max, no_event_tick) 2516 end 2517 def get_eventloop_weight 2518 __getip.get_eventloop_weight 2519 end 2520end 2521 2522# class methods to delegate to TclTkIp 2523class << MultiTkIp 2524 def method_missing(id, *args) 2525 __getip.__send__(id, *args) 2526 end 2527 2528 def make_safe 2529 __getip.make_safe 2530 end 2531 2532 def safe? 2533 __getip.safe? 2534 end 2535 2536 def safe_base? 2537 begin 2538 __getip.safe_base? 2539 rescue 2540 false 2541 end 2542 end 2543 2544 def allow_ruby_exit? 2545 __getip.allow_ruby_exit? 2546 end 2547 2548 def allow_ruby_exit= (mode) 2549 __getip.allow_ruby_exit = mode 2550 end 2551 2552 def delete 2553 __getip.delete 2554 end 2555 2556 def deleted? 2557 __getip.deleted? 2558 end 2559 2560 def has_mainwindow? 2561 __getip.has_mainwindow? 2562 end 2563 2564 def invalid_namespace? 2565 __getip.invalid_namespace? 2566 end 2567 2568 def abort(msg = nil) 2569 __getip.abort(msg) 2570 end 2571 2572 def exit(st = true) 2573 __getip.exit(st) 2574 end 2575 2576 def exit!(st = false) 2577 __getip.exit!(st) 2578 end 2579 2580 def restart(app_name = nil, keys = {}) 2581 init_ip_internal 2582 2583 __getip._invoke('set', 'argv0', app_name) if app_name 2584 if keys.kind_of?(Hash) 2585 __getip._invoke('set', 'argv', _keys2opts(keys)) 2586 end 2587 2588 __getip.restart 2589 end 2590 2591 def _eval(str) 2592 __getip._eval(str) 2593 end 2594 2595 def _invoke(*args) 2596 __getip._invoke(*args) 2597 end 2598 2599 def _eval_without_enc(str) 2600 __getip._eval_without_enc(str) 2601 end 2602 2603 def _invoke_without_enc(*args) 2604 __getip._invoke_without_enc(*args) 2605 end 2606 2607 def _eval_with_enc(str) 2608 __getip._eval_with_enc(str) 2609 end 2610 2611 def _invoke_with_enc(*args) 2612 __getip._invoke_with_enc(*args) 2613 end 2614 2615 def _toUTF8(str, encoding=nil) 2616 __getip._toUTF8(str, encoding) 2617 end 2618 2619 def _fromUTF8(str, encoding=nil) 2620 __getip._fromUTF8(str, encoding) 2621 end 2622 2623 def _thread_vwait(var) 2624 __getip._thread_vwait(var) 2625 end 2626 2627 def _thread_tkwait(mode, target) 2628 __getip._thread_tkwait(mode, target) 2629 end 2630 2631 def _return_value 2632 __getip._return_value 2633 end 2634 2635 def _get_variable(var, flag) 2636 __getip._get_variable(var, flag) 2637 end 2638 def _get_variable2(var, idx, flag) 2639 __getip._get_variable2(var, idx, flag) 2640 end 2641 def _set_variable(var, value, flag) 2642 __getip._set_variable(var, value, flag) 2643 end 2644 def _set_variable2(var, idx, value, flag) 2645 __getip._set_variable2(var, idx, value, flag) 2646 end 2647 def _unset_variable(var, flag) 2648 __getip._unset_variable(var, flag) 2649 end 2650 def _unset_variable2(var, idx, flag) 2651 __getip._unset_variable2(var, idx, flag) 2652 end 2653 2654 def _get_global_var(var) 2655 __getip._get_global_var(var) 2656 end 2657 def _get_global_var2(var, idx) 2658 __getip._get_global_var2(var, idx) 2659 end 2660 def _set_global_var(var, value) 2661 __getip._set_global_var(var, value) 2662 end 2663 def _set_global_var2(var, idx, value) 2664 __getip._set_global_var2(var, idx, value) 2665 end 2666 def _unset_global_var(var) 2667 __getip._unset_global_var(var) 2668 end 2669 def _unset_global_var2(var, idx) 2670 __getip._unset_global_var2(var, idx) 2671 end 2672 2673 def _make_menu_embeddable(menu_path) 2674 __getip._make_menu_embeddable(menu_path) 2675 end 2676 2677 def _split_tklist(str) 2678 __getip._split_tklist(str) 2679 end 2680 def _merge_tklist(*args) 2681 __getip._merge_tklist(*args) 2682 end 2683 def _conv_listelement(arg) 2684 __getip._conv_listelement(arg) 2685 end 2686 2687 def _create_console 2688 __getip._create_console 2689 end 2690end 2691 2692 2693# wrap methods on TclTkLib : not permit calling TclTkLib module methods 2694class << TclTkLib 2695 def mainloop(check_root = true) 2696 MultiTkIp.mainloop(check_root) 2697 end 2698 def mainloop_watchdog(check_root = true) 2699 MultiTkIp.mainloop_watchdog(check_root) 2700 end 2701 def do_one_event(flag = TclTkLib::EventFlag::ALL) 2702 MultiTkIp.do_one_event(flag) 2703 end 2704 #def mainloop_abort_on_exception 2705 # MultiTkIp.mainloop_abort_on_exception 2706 #end 2707 #def mainloop_abort_on_exception=(mode) 2708 # MultiTkIp.mainloop_abort_on_exception=(mode) 2709 #end 2710 def set_eventloop_tick(tick) 2711 MultiTkIp.set_eventloop_tick(tick) 2712 end 2713 def get_eventloop_tick 2714 MultiTkIp.get_eventloop_tick 2715 end 2716 def set_no_event_wait(tick) 2717 MultiTkIp.set_no_event_wait(tick) 2718 end 2719 def get_no_event_wait 2720 MultiTkIp.get_no_event_wait 2721 end 2722 def set_eventloop_weight(loop_max, no_event_tick) 2723 MultiTkIp.set_eventloop_weight(loop_max, no_event_tick) 2724 end 2725 def get_eventloop_weight 2726 MultiTkIp.get_eventloop_weight 2727 end 2728 def restart(*args) 2729 MultiTkIp.restart(*args) 2730 end 2731 2732 def _merge_tklist(*args) 2733 MultiTkIp._merge_tklist(*args) 2734 end 2735 def _conv_listelement(arg) 2736 MultiTkIp._conv_listelement(arg) 2737 end 2738end 2739 2740 2741# depend on TclTkIp 2742class MultiTkIp 2743# def mainloop(check_root = true, restart_on_dead = true) 2744 def mainloop(check_root = true, restart_on_dead = false) 2745 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2746 2747 if WITH_RUBY_VM ### Ruby 1.9 !!!!!!!!!!! 2748 return @interp_thread.value if @interp_thread 2749 end 2750 2751 #return self if self.slave? 2752 #return self if self != @@DEFAULT_MASTER 2753 if self != @@DEFAULT_MASTER 2754 if @wait_on_mainloop[0] 2755 begin 2756 @wait_on_mainloop[1] += 1 2757 if $SAFE >= 4 2758 _receiver_mainloop(check_root).join 2759 else 2760 @cmd_queue.enq([@system, 'call_mainloop', 2761 Thread.current, check_root]) 2762 Thread.stop 2763 end 2764 rescue MultiTkIp_OK => ret 2765 # return value 2766 if ret.value.kind_of?(Thread) 2767 return ret.value.value 2768 else 2769 return ret.value 2770 end 2771 rescue SystemExit => e 2772 # exit IP 2773 warn("Warning: " + e.inspect + " on " + self.inspect) if $DEBUG 2774 begin 2775 self._eval_without_enc('exit') 2776 rescue Exception 2777 end 2778 self.delete 2779 rescue StandardError => e 2780 if $DEBUG 2781 warn("Warning: " + e.class.inspect + 2782 ((e.message.length > 0)? ' "' + e.message + '"': '') + 2783 " on " + self.inspect) 2784 end 2785 return e 2786 rescue Exception => e 2787 return e 2788 ensure 2789 @wait_on_mainloop[1] -= 1 2790 end 2791 end 2792 return 2793 end 2794 2795 unless restart_on_dead 2796 @wait_on_mainloop[1] += 1 2797=begin 2798 begin 2799 @interp.mainloop(check_root) 2800 rescue StandardError => e 2801 if $DEBUG 2802 warn("Warning: " + e.class.inspect + 2803 ((e.message.length > 0)? ' "' + e.message + '"': '') + 2804 " on " + self.inspect) 2805 end 2806 end 2807=end 2808 begin 2809 @interp.mainloop(check_root) 2810 ensure 2811 @wait_on_mainloop[1] -= 1 2812 end 2813 else 2814 loop do 2815 break unless self.alive? 2816 if check_root 2817 begin 2818 break if TclTkLib.num_of_mainwindows == 0 2819 rescue StandardError 2820 break 2821 end 2822 end 2823 break if @interp.deleted? 2824 begin 2825 @wait_on_mainloop[1] += 1 2826 @interp.mainloop(check_root) 2827 rescue StandardError => e 2828 if TclTkLib.mainloop_abort_on_exception != nil 2829 #STDERR.print("Warning: Tk mainloop receives ", $!.class.inspect, 2830 # " exception (ignore) : ", $!.message, "\n"); 2831 if $DEBUG 2832 warn("Warning: Tk mainloop receives " << e.class.inspect << 2833 " exception (ignore) : " << e.message); 2834 end 2835 end 2836 #raise e 2837 rescue Exception => e 2838=begin 2839 if TclTkLib.mainloop_abort_on_exception != nil 2840 #STDERR.print("Warning: Tk mainloop receives ", $!.class.inspect, 2841 # " exception (ignore) : ", $!.message, "\n"); 2842 if $DEBUG 2843 warn("Warning: Tk mainloop receives " << e.class.inspect << 2844 " exception (ignore) : " << e.message); 2845 end 2846 end 2847=end 2848 raise e 2849 ensure 2850 @wait_on_mainloop[1] -= 1 2851 Thread.pass # avoid eventloop conflict 2852 end 2853 end 2854 end 2855 self 2856 end 2857 2858 def make_safe 2859 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2860 @interp.make_safe 2861 end 2862 2863 def safe? 2864 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2865 @interp.safe? 2866 end 2867 2868 def safe_base? 2869 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2870 @safe_base 2871 end 2872 2873 def allow_ruby_exit? 2874 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2875 @interp.allow_ruby_exit? 2876 end 2877 2878 def allow_ruby_exit= (mode) 2879 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2880 @interp.allow_ruby_exit = mode 2881 end 2882 2883 def delete 2884 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2885 @slave_ip_tbl.each{|name, subip| 2886 _destroy_slaves_of_slaveIP(subip) 2887=begin 2888 begin 2889 subip._invoke('destroy', '.') unless subip.deleted? 2890 rescue Exception 2891 end 2892=end 2893 begin 2894 # subip._eval_without_enc("foreach i [after info] {after cancel $i}") 2895 unless subip.deleted? 2896 after_ids = subip._eval_without_enc("after info") 2897 subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") 2898 end 2899 rescue Exception 2900 end 2901 2902 # safe_base? 2903 if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' 2904 begin 2905 @interp._eval_without_enc("::safe::interpDelete #{name}") 2906 rescue Exception 2907 else 2908 next if subip.deleted? 2909 end 2910 end 2911 if subip.respond_to?(:safe_base?) && subip.safe_base? && 2912 !subip.deleted? 2913 # do 'exit' to call the delete_hook procedure 2914 begin 2915 subip._eval_without_enc('exit') 2916 rescue Exception 2917 end 2918 else 2919 begin 2920 subip.delete unless subip.deleted? 2921 rescue Exception 2922 end 2923 end 2924 } 2925 2926 begin 2927 # @interp._eval_without_enc("foreach i [after info] {after cancel $i}") 2928 after_ids = @interp._eval_without_enc("after info") 2929 @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") 2930 rescue Exception 2931 end 2932 2933 begin 2934 @interp._invoke('destroy', '.') unless @interp.deleted? 2935 rescue Exception 2936 end 2937 2938 if @safe_base && !@interp.deleted? 2939 # do 'exit' to call the delete_hook procedure 2940 @interp._eval_without_enc('exit') 2941 end 2942 @interp.delete 2943 self 2944 end 2945 2946 def deleted? 2947 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2948 @interp.deleted? 2949 end 2950 2951 def has_mainwindow? 2952 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2953 @interp.has_mainwindow? 2954 end 2955 2956 def invalid_namespace? 2957 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2958 @interp.invalid_namespace? 2959 end 2960 2961 def abort(msg = nil) 2962 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2963 if master? && !safe? && allow_ruby_exit? 2964 if msg 2965 Kernel.abort(msg) 2966 else 2967 Kernel.abort 2968 end 2969 else 2970 # ignore msg 2971 delete 2972 1 2973 end 2974 end 2975 2976 def exit(st = true) 2977 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2978 if master? && !safe? && allow_ruby_exit? 2979 Kernel.exit(st) 2980 else 2981 delete 2982 st 2983 end 2984 end 2985 2986 def exit!(st = false) 2987 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2988 if master? && !safe? && allow_ruby_exit? 2989 Kernel.exit!(st) 2990 else 2991 delete 2992 st 2993 end 2994 end 2995 2996 def restart(app_name = nil, keys = {}) 2997 raise SecurityError, "no permission to manipulate" unless self.manipulable? 2998 2999 _init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS) 3000 3001 @interp._invoke('set', 'argv0', app_name) if app_name 3002 if keys.kind_of?(Hash) 3003 @interp._invoke('set', 'argv', _keys2opts(keys)) 3004 end 3005 3006 @interp.restart 3007 end 3008 3009 def __eval(str) 3010 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3011 @interp.__eval(str) 3012 end 3013 3014 def __invoke(*args) 3015 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3016 @interp.__invoke(*args) 3017 end 3018 3019 def _eval(str) 3020 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3021 @interp._eval(str) 3022 end 3023 3024 def _invoke(*args) 3025 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3026 @interp._invoke(*args) 3027 end 3028 3029 def _eval_without_enc(str) 3030 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3031 @interp._eval_without_enc(str) 3032 end 3033 3034 def _invoke_without_enc(*args) 3035 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3036 @interp._invoke_without_enc(*args) 3037 end 3038 3039 def _eval_with_enc(str) 3040 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3041 @interp._eval_with_enc(str) 3042 end 3043 3044 def _invoke_with_enc(*args) 3045 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3046 @interp._invoke_with_enc(*args) 3047 end 3048 3049 def _toUTF8(str, encoding=nil) 3050 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3051 @interp._toUTF8(str, encoding) 3052 end 3053 3054 def _fromUTF8(str, encoding=nil) 3055 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3056 @interp._fromUTF8(str, encoding) 3057 end 3058 3059 def _thread_vwait(var) 3060 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3061 @interp._thread_vwait(var) 3062 end 3063 3064 def _thread_tkwait(mode, target) 3065 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3066 @interp._thread_tkwait(mode, target) 3067 end 3068 3069 def _return_value 3070 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3071 @interp._return_value 3072 end 3073 3074 def _get_variable(var, flag) 3075 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3076 @interp._get_variable(var, flag) 3077 end 3078 def _get_variable2(var, idx, flag) 3079 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3080 @interp._get_variable2(var, idx, flag) 3081 end 3082 def _set_variable(var, value, flag) 3083 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3084 @interp._set_variable(var, value, flag) 3085 end 3086 def _set_variable2(var, idx, value, flag) 3087 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3088 @interp._set_variable2(var, idx, value, flag) 3089 end 3090 def _unset_variable(var, flag) 3091 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3092 @interp._unset_variable(var, flag) 3093 end 3094 def _unset_variable2(var, idx, flag) 3095 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3096 @interp._unset_variable2(var, idx, flag) 3097 end 3098 3099 def _get_global_var(var) 3100 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3101 @interp._get_global_var(var) 3102 end 3103 def _get_global_var2(var, idx) 3104 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3105 @interp._get_global_var2(var, idx) 3106 end 3107 def _set_global_var(var, value) 3108 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3109 @interp._set_global_var(var, value) 3110 end 3111 def _set_global_var2(var, idx, value) 3112 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3113 @interp._set_global_var2(var, idx, value) 3114 end 3115 def _unset_global_var(var) 3116 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3117 @interp._unset_global_var(var) 3118 end 3119 def _unset_global_var2(var, idx) 3120 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3121 @interp._unset_global_var2(var, idx) 3122 end 3123 3124 def _make_menu_embeddable(menu_path) 3125 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3126 @interp._make_menu_embeddable(menu_path) 3127 end 3128 3129 def _split_tklist(str) 3130 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3131 @interp._split_tklist(str) 3132 end 3133 def _merge_tklist(*args) 3134 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3135 @interp._merge_tklist(*args) 3136 end 3137 def _conv_listelement(arg) 3138 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3139 @interp._conv_listelement(arg) 3140 end 3141end 3142 3143 3144# interp command support 3145class MultiTkIp 3146 def _lst2ary(str) 3147 return [] if str == "" 3148 idx = str.index('{') 3149 while idx and idx > 0 and str[idx-1] == ?\\ 3150 idx = str.index('{', idx+1) 3151 end 3152 return str.split unless idx 3153 3154 list = str[0,idx].split 3155 str = str[idx+1..-1] 3156 i = -1 3157 brace = 1 3158 str.each_byte {|c| 3159 c = c.chr 3160 i += 1 3161 brace += 1 if c == '{' 3162 brace -= 1 if c == '}' 3163 break if brace == 0 3164 } 3165 if i == 0 3166 list.push '' 3167 elsif str[0, i] == ' ' 3168 list.push ' ' 3169 else 3170 list.push str[0..i-1] 3171 end 3172 #list += _lst2ary(str[i+1..-1]) 3173 list.concat(_lst2ary(str[i+1..-1])) 3174 list 3175 end 3176 private :_lst2ary 3177 3178 def _slavearg(slave) 3179 if slave.kind_of?(MultiTkIp) 3180 slave.path 3181 elsif slave.kind_of?(String) 3182 slave 3183 else 3184 slave.to_s 3185 end 3186 end 3187 private :_slavearg 3188 3189 def alias_info(slave, cmd_name) 3190 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3191 _lst2ary(@interp._invoke('interp', 'alias', _slavearg(slave), cmd_name)) 3192 end 3193 def self.alias_info(slave, cmd_name) 3194 __getip.alias_info(slave, cmd_name) 3195 end 3196 3197 def alias_delete(slave, cmd_name) 3198 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3199 @interp._invoke('interp', 'alias', _slavearg(slave), cmd_name, '') 3200 self 3201 end 3202 def self.alias_delete(slave, cmd_name) 3203 __getip.alias_delete(slave, cmd_name) 3204 self 3205 end 3206 3207 def def_alias(slave, new_cmd, org_cmd, *args) 3208 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3209 ret = @interp._invoke('interp', 'alias', _slavearg(slave), new_cmd, 3210 '', org_cmd, *args) 3211 (ret == new_cmd)? self: nil 3212 end 3213 def self.def_alias(slave, new_cmd, org_cmd, *args) 3214 ret = __getip.def_alias(slave, new_cmd, org_cmd, *args) 3215 (ret == new_cmd)? self: nil 3216 end 3217 3218 def aliases(slave = '') 3219 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3220 _lst2ary(@interp._invoke('interp', 'aliases', _slavearg(slave))) 3221 end 3222 def self.aliases(slave = '') 3223 __getip.aliases(slave) 3224 end 3225 3226 def delete_slaves(*args) 3227 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3228 slaves = args.collect{|s| _slavearg(s)} 3229 @interp._invoke('interp', 'delete', *slaves) if slaves.size > 0 3230 self 3231 end 3232 def self.delete_slaves(*args) 3233 __getip.delete_slaves(*args) 3234 self 3235 end 3236 3237 def exist?(slave = '') 3238 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3239 ret = @interp._invoke('interp', 'exists', _slavearg(slave)) 3240 (ret == '1')? true: false 3241 end 3242 def self.exist?(slave = '') 3243 __getip.exist?(slave) 3244 end 3245 3246 def delete_cmd(slave, cmd) 3247 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3248 slave_invoke = @interp._invoke('list', 'rename', cmd, '') 3249 @interp._invoke('interp', 'eval', _slavearg(slave), slave_invoke) 3250 self 3251 end 3252 def self.delete_cmd(slave, cmd) 3253 __getip.delete_cmd(slave, cmd) 3254 self 3255 end 3256 3257 def expose_cmd(slave, cmd, aliasname = nil) 3258 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3259 if aliasname 3260 @interp._invoke('interp', 'expose', _slavearg(slave), cmd, aliasname) 3261 else 3262 @interp._invoke('interp', 'expose', _slavearg(slave), cmd) 3263 end 3264 self 3265 end 3266 def self.expose_cmd(slave, cmd, aliasname = nil) 3267 __getip.expose_cmd(slave, cmd, aliasname) 3268 self 3269 end 3270 3271 def hide_cmd(slave, cmd, aliasname = nil) 3272 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3273 if aliasname 3274 @interp._invoke('interp', 'hide', _slavearg(slave), cmd, aliasname) 3275 else 3276 @interp._invoke('interp', 'hide', _slavearg(slave), cmd) 3277 end 3278 self 3279 end 3280 def self.hide_cmd(slave, cmd, aliasname = nil) 3281 __getip.hide_cmd(slave, cmd, aliasname) 3282 self 3283 end 3284 3285 def hidden_cmds(slave = '') 3286 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3287 _lst2ary(@interp._invoke('interp', 'hidden', _slavearg(slave))) 3288 end 3289 def self.hidden_cmds(slave = '') 3290 __getip.hidden_cmds(slave) 3291 end 3292 3293 def invoke_hidden(slave, cmd, *args) 3294 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3295 if args[-1].kind_of?(Hash) 3296 keys = _symbolkey2str(args.pop) 3297 else 3298 keys = [] 3299 end 3300 keys << _slavearg(slave) 3301 if Tk::TCL_MAJOR_VERSION > 8 || 3302 (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION >= 5) 3303 keys << '--' 3304 end 3305 keys << cmd 3306 keys.concat(args) 3307 @interp._invoke('interp', 'invokehidden', *keys) 3308 end 3309 def self.invoke_hidden(slave, cmd, *args) 3310 __getip.invoke_hidden(slave, cmd, *args) 3311 end 3312 3313 def invoke_hidden_on_global(slave, cmd, *args) 3314 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3315 if args[-1].kind_of?(Hash) 3316 keys = _symbolkey2str(args.pop) 3317 else 3318 keys = [] 3319 end 3320 keys << _slavearg(slave) 3321 keys << '-global' 3322 if Tk::TCL_MAJOR_VERSION > 8 || 3323 (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION >= 5) 3324 keys << '--' 3325 end 3326 keys << cmd 3327 keys.concat(args) 3328 @interp._invoke('interp', 'invokehidden', *keys) 3329 end 3330 def self.invoke_hidden_on_global(slave, cmd, *args) 3331 __getip.invoke_hidden_on_global(slave, cmd, *args) 3332 end 3333 3334 def invoke_hidden_on_namespace(slave, ns, cmd, *args) 3335 # for Tcl8.5 or later 3336 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3337 if args[-1].kind_of?(Hash) 3338 keys = _symbolkey2str(args.pop) 3339 else 3340 keys = [] 3341 end 3342 keys << _slavearg(slave) 3343 keys << '-namespace' << TkComm._get_eval_string(ns) 3344 keys << '--' << cmd 3345 keys.concat(args) 3346 @interp._invoke('interp', 'invokehidden', *keys) 3347 end 3348 def self.invoke_hidden_on_namespace(slave, ns, cmd, *args) 3349 __getip.invoke_hidden_on_namespace(slave, ns, cmd, *args) 3350 end 3351 3352 def mark_trusted(slave = '') 3353 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3354 @interp._invoke('interp', 'marktrusted', _slavearg(slave)) 3355 self 3356 end 3357 def self.mark_trusted(slave = '') 3358 __getip.mark_trusted(slave) 3359 self 3360 end 3361 3362 def set_bgerror_handler(cmd = Proc.new, slave = nil, &b) 3363 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3364 3365 unless TkComm._callback_entry?(cmd) 3366 if !slave && b 3367 slave = cmd 3368 cmd = Proc.new(&b) 3369 end 3370 end 3371 slave = '' unless slave 3372 3373 @interp._invoke('interp', 'bgerror', _slavearg(slave), cmd) 3374 end 3375 def self.bgerror(cmd = Proc.new, slave = nil, &b) 3376 __getip.bgerror(cmd, slave, &b) 3377 end 3378 3379 def get_bgerror_handler(slave = '') 3380 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3381 procedure(@interp._invoke('interp', 'bgerror', _slavearg(slave))) 3382 end 3383 def self.bgerror(slave = '') 3384 __getip.bgerror(slave) 3385 end 3386 3387 def set_limit(limit_type, slave = '', opts = {}) 3388 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3389 @interp._invoke('interp', 'limit', _slavearg(slave), limit_type, opts) 3390 end 3391 def self.set_limit(limit_type, slave = '', opts = {}) 3392 __getip.set_limit(limit_type, slave, opts) 3393 end 3394 3395 def get_limit(limit_type, slave = '', slot = nil) 3396 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3397 3398 if slot 3399 num_or_str(@interp._invoke('interp', 'limit', _slavearg(slave), 3400 limit_type, slot)) 3401 else 3402 l = @interp._split_tklist(@interp._invoke_without_enc('interp', 'limit', 3403 _slavearg(slave), 3404 limit_type)) 3405 l.map!{|s| _fromUTF8(s)} 3406 r = {} 3407 until l.empty? 3408 key = l.shift[1..-1] 3409 val = l.shift 3410 val = num_or_str(val) if val 3411 r[key] = val 3412 end 3413 r 3414 end 3415 end 3416 def self.get_limit(limit_type, slave = '', slot = nil) 3417 __getip.get_limit(limit_type, slave, slot) 3418 end 3419 3420 def recursion_limit(slave = '', limit = None) 3421 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3422 number(@interp._invoke('interp', 'recursionlimit', 3423 _slavearg(slave), limit)) 3424 end 3425 def self.recursion_limit(slave = '', limit = None) 3426 __getip.recursion_limit(slave) 3427 end 3428 3429 def alias_target(aliascmd, slave = '') 3430 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3431 @interp._invoke('interp', 'target', _slavearg(slave), aliascmd) 3432 end 3433 def self.alias_target(aliascmd, slave = '') 3434 __getip.alias_target(aliascmd, slave) 3435 end 3436 3437 def share_stdin(dist, src = '') 3438 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3439 @interp._invoke('interp', 'share', src, 'stdin', dist) 3440 self 3441 end 3442 def self.share_stdin(dist, src = '') 3443 __getip.share_stdin(dist, src) 3444 self 3445 end 3446 3447 def share_stdout(dist, src = '') 3448 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3449 @interp._invoke('interp', 'share', src, 'stdout', dist) 3450 self 3451 end 3452 def self.share_stdout(dist, src = '') 3453 __getip.share_stdout(dist, src) 3454 self 3455 end 3456 3457 def share_stderr(dist, src = '') 3458 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3459 @interp._invoke('interp', 'share', src, 'stderr', dist) 3460 self 3461 end 3462 def self.share_stderr(dist, src = '') 3463 __getip.share_stderr(dist, src) 3464 self 3465 end 3466 3467 def transfer_stdin(dist, src = '') 3468 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3469 @interp._invoke('interp', 'transfer', src, 'stdin', dist) 3470 self 3471 end 3472 def self.transfer_stdin(dist, src = '') 3473 __getip.transfer_stdin(dist, src) 3474 self 3475 end 3476 3477 def transfer_stdout(dist, src = '') 3478 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3479 @interp._invoke('interp', 'transfer', src, 'stdout', dist) 3480 self 3481 end 3482 def self.transfer_stdout(dist, src = '') 3483 __getip.transfer_stdout(dist, src) 3484 self 3485 end 3486 3487 def transfer_stderr(dist, src = '') 3488 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3489 @interp._invoke('interp', 'transfer', src, 'stderr', dist) 3490 self 3491 end 3492 def self.transfer_stderr(dist, src = '') 3493 __getip.transfer_stderr(dist, src) 3494 self 3495 end 3496 3497 def share_stdio(dist, src = '') 3498 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3499 @interp._invoke('interp', 'share', src, 'stdin', dist) 3500 @interp._invoke('interp', 'share', src, 'stdout', dist) 3501 @interp._invoke('interp', 'share', src, 'stderr', dist) 3502 self 3503 end 3504 def self.share_stdio(dist, src = '') 3505 __getip.share_stdio(dist, src) 3506 self 3507 end 3508 3509 def transfer_stdio(dist, src = '') 3510 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3511 @interp._invoke('interp', 'transfer', src, 'stdin', dist) 3512 @interp._invoke('interp', 'transfer', src, 'stdout', dist) 3513 @interp._invoke('interp', 'transfer', src, 'stderr', dist) 3514 self 3515 end 3516 def self.transfer_stdio(dist, src = '') 3517 __getip.transfer_stdio(dist, src) 3518 self 3519 end 3520end 3521 3522 3523# Safe Base :: manipulating safe interpreter 3524class MultiTkIp 3525 def safeip_configure(slot, value=None) 3526 # use for '-noStatics' option ==> {statics=>false} 3527 # for '-nestedLoadOk' option ==> {nested=>true} 3528 if slot.kind_of?(Hash) 3529 ip = MultiTkIp.__getip 3530 ip._eval('::safe::interpConfigure ' + @ip_name + ' ' + _keys2opts(slot)) 3531 else 3532 ip._eval('::safe::interpConfigure ' + @ip_name + ' ' + 3533 "-#{slot} #{_get_eval_string(value)}") 3534 end 3535 self 3536 end 3537 3538 def safeip_configinfo(slot = nil) 3539 ip = MultiTkIp.__getip 3540 ret = {} 3541 if slot 3542 conf = _lst2ary(ip._eval("::safe::interpConfigure " + 3543 @ip_name + " -#{slot}")) 3544 if conf[0] == '-deleteHook' 3545=begin 3546 if conf[1] =~ /^rb_out\S* (c(_\d+_)?\d+)/ 3547 ret[conf[0][1..-1]] = MultiTkIp._tk_cmd_tbl[$1] 3548=end 3549 if conf[1] =~ /rb_out\S*(?:\s+(::\S*|[{](::.*)[}]|["](::.*)["]))? (c(_\d+_)?(\d+))/ 3550 ret[conf[0][1..-1]] = MultiTkIp._tk_cmd_tbl[$4] 3551 else 3552 ret[conf[0][1..-1]] = conf[1] 3553 end 3554 else 3555 ret[conf[0][1..-1]] = conf[1] 3556 end 3557 else 3558 Hash[*_lst2ary(ip._eval("::safe::interpConfigure " + 3559 @ip_name))].each{|k, v| 3560 if k == '-deleteHook' 3561=begin 3562 if v =~ /^rb_out\S* (c(_\d+_)?\d+)/ 3563 ret[k[1..-1]] = MultiTkIp._tk_cmd_tbl[$1] 3564=end 3565 if v =~ /rb_out\S*(?:\s+(::\S*|[{](::.*)[}]|["](::.*)["]))? (c(_\d+_)?(\d+))/ 3566 ret[k[1..-1]] = MultiTkIp._tk_cmd_tbl[$4] 3567 else 3568 ret[k[1..-1]] = v 3569 end 3570 else 3571 ret[k[1..-1]] = v 3572 end 3573 } 3574 end 3575 ret 3576 end 3577 3578 def safeip_delete 3579 ip = MultiTkIp.__getip 3580 ip._eval("::safe::interpDelete " + @ip_name) 3581 end 3582 3583 def safeip_add_to_access_path(dir) 3584 ip = MultiTkIp.__getip 3585 ip._eval("::safe::interpAddToAccessPath #{@ip_name} #{dir}") 3586 end 3587 3588 def safeip_find_in_access_path(dir) 3589 ip = MultiTkIp.__getip 3590 ip._eval("::safe::interpFindInAccessPath #{@ip_name} #{dir}") 3591 end 3592 3593 def safeip_set_log_cmd(cmd = Proc.new) 3594 ip = MultiTkIp.__getip 3595 ip._eval("::safe::setLogCmd #{@ip_name} #{_get_eval_string(cmd)}") 3596 end 3597end 3598 3599 3600# encoding convert 3601class << MultiTkIp 3602 def encoding_table 3603 __getip.encoding_table 3604 end 3605 3606 def force_default_encoding=(mode) 3607 __getip.force_default_encoding=(mode) 3608 end 3609 3610 def force_default_encoding? 3611 __getip.force_default_encoding? 3612 end 3613 3614 def default_encoding=(enc) 3615 __getip.default_encoding=(enc) 3616 end 3617 3618 def encoding=(enc) 3619 __getip.encoding=(enc) 3620 end 3621 3622 def encoding_name 3623 __getip.encoding_name 3624 end 3625 3626 def encoding_obj 3627 __getip.encoding_obj 3628 end 3629 alias encoding encoding_name 3630 alias default_encoding encoding_name 3631 3632 def encoding_convertfrom(str, enc=None) 3633 __getip.encoding_convertfrom(str, enc) 3634 end 3635 alias encoding_convert_from encoding_convertfrom 3636 3637 def encoding_convertto(str, enc=None) 3638 __getip.encoding_convertto(str, enc) 3639 end 3640 alias encoding_convert_to encoding_convertto 3641end 3642class MultiTkIp 3643 def encoding_table 3644 @interp.encoding_table 3645 end 3646 3647 def force_default_encoding=(mode) 3648 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3649 @interp.force_default_encoding = mode 3650 end 3651 def force_default_encoding? 3652 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3653 @interp.force_default_encoding? 3654 end 3655 3656 def default_encoding=(enc) 3657 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3658 @interp.default_encoding = enc 3659 end 3660 3661 def encoding=(enc) 3662 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3663 @interp.encoding = enc 3664 end 3665 def encoding_name 3666 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3667 @interp.encoding_name 3668 end 3669 def encoding_obj 3670 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3671 @interp.encoding_obj 3672 end 3673 alias encoding encoding_name 3674 alias default_encoding encoding_name 3675 3676 def encoding_convertfrom(str, enc=None) 3677 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3678 @interp.encoding_convertfrom(str, enc) 3679 end 3680 alias encoding_convert_from encoding_convertfrom 3681 3682 def encoding_convertto(str, enc=None) 3683 raise SecurityError, "no permission to manipulate" unless self.manipulable? 3684 @interp.encoding_convertto(str, enc) 3685 end 3686 alias encoding_convert_to encoding_convertto 3687end 3688 3689 3690# remove methods for security 3691=begin 3692class MultiTkIp 3693 INTERP_THREAD = @@DEFAULT_MASTER.instance_variable_get('@interp_thread') 3694 INTERP_MUTEX = INTERP_THREAD[:mutex] 3695 INTERP_ROOT_CHECK = INTERP_THREAD[:root_check] 3696 3697 # undef_method :instance_eval 3698 undef_method :instance_variable_get 3699 undef_method :instance_variable_set 3700end 3701 3702module TkCore 3703 if MultiTkIp::WITH_RUBY_VM && 3704 ! MultiTkIp::RUN_EVENTLOOP_ON_MAIN_THREAD ### check Ruby 1.9 !!!!!!! 3705 INTERP_THREAD = MultiTkIp::INTERP_THREAD 3706 INTERP_MUTEX = MultiTkIp::INTERP_MUTEX 3707 INTERP_ROOT_CHECK = MultiTkIp::INTERP_ROOT_CHECK 3708 end 3709end 3710class MultiTkIp 3711 remove_const(:INTERP_THREAD) 3712 remove_const(:INTERP_MUTEX) 3713 remove_const(:INTERP_ROOT_CHECK) 3714end 3715=end 3716if MultiTkIp::WITH_RUBY_VM && 3717 ! MultiTkIp::RUN_EVENTLOOP_ON_MAIN_THREAD ### check Ruby 1.9 !!!!!!! 3718 class MultiTkIp 3719 INTERP_THREAD = @@DEFAULT_MASTER.instance_variable_get('@interp_thread') 3720 INTERP_THREAD_STATUS = INTERP_THREAD[:status] 3721 INTERP_MUTEX = INTERP_THREAD[:mutex] 3722 INTERP_ROOT_CHECK = INTERP_THREAD[:root_check] 3723 end 3724 module TkCore 3725 INTERP_THREAD = MultiTkIp::INTERP_THREAD 3726 INTERP_THREAD_STATUS = MultiTkIp::INTERP_THREAD_STATUS 3727 INTERP_MUTEX = MultiTkIp::INTERP_MUTEX 3728 INTERP_ROOT_CHECK = MultiTkIp::INTERP_ROOT_CHECK 3729 end 3730 class MultiTkIp 3731 remove_const(:INTERP_THREAD) 3732 remove_const(:INTERP_THREAD_STATUS) 3733 remove_const(:INTERP_MUTEX) 3734 remove_const(:INTERP_ROOT_CHECK) 3735 end 3736end 3737 3738class MultiTkIp 3739 # undef_method :instance_eval 3740 undef_method :instance_variable_get 3741 undef_method :instance_variable_set 3742end 3743# end of MultiTkIp definition 3744 3745# defend against modification 3746#MultiTkIp.freeze 3747#TclTkLib.freeze 3748 3749######################################## 3750# start Tk which depends on MultiTkIp 3751module TkCore 3752 INTERP = MultiTkIp 3753end 3754require 'tk' 3755