summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
Diffstat (limited to 'misc')
-rw-r--r--misc/.vscode/launch.json13
-rw-r--r--misc/.vscode/settings.json8
-rw-r--r--misc/.vscode/tasks.json14
-rw-r--r--misc/README19
-rw-r--r--misc/call_fuzzer.rb372
-rwxr-xr-xmisc/call_fuzzer.sh13
-rwxr-xr-xmisc/expand_tabs.rb208
-rw-r--r--misc/gdb.py181
-rw-r--r--misc/inf-ruby.el418
-rwxr-xr-xmisc/lldb_cruby.py674
-rw-r--r--misc/lldb_disasm.py250
-rw-r--r--misc/lldb_rb/commands/command_template.py30
-rw-r--r--misc/lldb_rb/commands/heap_page_command.py26
-rw-r--r--misc/lldb_rb/commands/print_flags_command.py31
-rw-r--r--misc/lldb_rb/commands/rb_id2str_command.py49
-rw-r--r--misc/lldb_rb/commands/rclass_ext_command.py14
-rw-r--r--misc/lldb_rb/commands/rp_command.py15
-rw-r--r--misc/lldb_rb/constants.py6
-rw-r--r--misc/lldb_rb/lldb_interface.py7
-rw-r--r--misc/lldb_rb/rb_base_command.py57
-rw-r--r--misc/lldb_rb/rb_heap_structs.py143
-rw-r--r--misc/lldb_rb/utils.py513
-rw-r--r--misc/rb_optparse.bash5
-rwxr-xr-x[-rw-r--r--]misc/rb_optparse.zsh15
-rw-r--r--misc/rdoc-mode.el166
-rw-r--r--misc/ruby-additional.el181
-rw-r--r--misc/ruby-electric.el583
-rw-r--r--misc/ruby-mode.el1584
-rw-r--r--misc/ruby-style.el32
-rw-r--r--misc/rubydb2x.el104
-rw-r--r--misc/rubydb3x.el115
-rw-r--r--misc/test_lldb_cruby.rb8
-rwxr-xr-xmisc/yjit_perf.py116
33 files changed, 3228 insertions, 2742 deletions
diff --git a/misc/.vscode/launch.json b/misc/.vscode/launch.json
deleted file mode 100644
index 51bfef09d7..0000000000
--- a/misc/.vscode/launch.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "version": "0.2.0",
- "configurations": [
- {
- "type": "lldb",
- "name": "Run ruby",
- "request": "launch",
- "program": "${workspaceFolder}/ruby",
- "args": ["test.rb"],
- "preLaunchTask": "${defaultBuildTask}"
- }
- ]
-}
diff --git a/misc/.vscode/settings.json b/misc/.vscode/settings.json
deleted file mode 100644
index 7b1a38c536..0000000000
--- a/misc/.vscode/settings.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "rust-analyzer.cargo.features": [
- "disasm",
- ],
- "rust-analyzer.cargo.unsetTest": [
- "yjit",
- ],
-}
diff --git a/misc/.vscode/tasks.json b/misc/.vscode/tasks.json
deleted file mode 100644
index 045fe7e5c0..0000000000
--- a/misc/.vscode/tasks.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "version": "2.0.0",
- "tasks": [
- {
- "label": "build",
- "type": "shell",
- "command": "make -j",
- "group": {
- "kind": "build",
- "isDefault": true
- }
- }
- ]
-}
diff --git a/misc/README b/misc/README
index 86b680e724..08a9010f58 100644
--- a/misc/README
+++ b/misc/README
@@ -1,7 +1,12 @@
-README this file
-rb_optparse.bash bash completion script
-rb_optparse.zsh zsh completion script
-ruby-style.el Ruby's C/C++ mode style for emacs
-lldb_cruby.py LLDB port of debug utility
-test_lldb_cruby.rb test file for LLDB port
-.vscode example VSCode config to debug Ruby
+README this file
+inf-ruby.el program to run ruby under emacs
+rb_optparse.bash bash completion script
+rb_optparse.zsh zsh completion script
+rdoc-mode.el RDoc mode for emacs
+ruby-mode.el ruby mode for emacs
+ruby-style.el Ruby's C/C++ mode style for emacs
+rubydb2x.el ruby debugger support for emacs 19.2x or before
+rubydb3x.el ruby debugger support for emacs 19.3x or later
+ruby-electric.el emacs minor mode providing electric commands
+
+Check out https://github.com/ruby-debug/ also.
diff --git a/misc/call_fuzzer.rb b/misc/call_fuzzer.rb
deleted file mode 100644
index c3f9f90490..0000000000
--- a/misc/call_fuzzer.rb
+++ /dev/null
@@ -1,372 +0,0 @@
-require 'optparse'
-require 'set'
-
-# Number of iterations to test
-num_iters = 10_000
-
-# Parse the command-line options
-OptionParser.new do |opts|
- opts.on("--num-iters=N") do |n|
- num_iters = n.to_i
- end
-end.parse!
-
-# Format large numbers with comma separators for readability
-def format_number(pad, number)
- s = number.to_s
- i = s.index('.') || s.size
- s.insert(i -= 3, ',') while i > 3
- s.rjust(pad, ' ')
-end
-
-# Wrap an integer to pass as argument
-# We use this so we can have some object arguments
-class IntWrapper
- def initialize(v)
- # Force the object to have a random shape
- if rand() < 50
- @v0 = 1
- end
- if rand() < 50
- @v1 = 1
- end
- if rand() < 50
- @v2 = 1
- end
- if rand() < 50
- @v3 = 1
- end
- if rand() < 50
- @v4 = 1
- end
- if rand() < 50
- @v5 = 1
- end
- if rand() < 50
- @v6 = 1
- end
-
- @value = v
- end
-
- attr_reader :value
-end
-
-# Generate a random argument value, integer or string or object
-def sample_arg()
- c = ['int', 'string', 'object'].sample()
-
- if c == 'int'
- return rand(0...100)
- end
-
- if c == 'string'
- return 'f' * rand(0...100)
- end
-
- if c == 'object'
- return IntWrapper.new(rand(0...100))
- end
-
- raise "should not get here"
-end
-
-# Evaluate the value of an argument with respect to the checksum
-def arg_val(arg)
- if arg.kind_of? Integer
- return arg
- end
-
- if arg.kind_of? String
- return arg.length
- end
-
- if arg.kind_of? Object
- return arg.value
- end
-
- raise "unknown arg type"
-end
-
-# List of parameters/arguments for a method
-class ParamList
- def initialize()
- self.sample_params()
- self.sample_args()
- end
-
- # Sample/generate a random set of parameters for a method
- def sample_params()
- # Choose how many positional arguments to use, and how many are optional
- num_pargs = rand(10)
- @opt_parg_idx = rand(num_pargs)
- @num_opt_pargs = rand(num_pargs + 1 - @opt_parg_idx)
- @num_pargs_req = num_pargs - @num_opt_pargs
- @pargs = (0...num_pargs).map do |i|
- {
- :name => "p#{i}",
- :optional => (i >= @opt_parg_idx && i < @opt_parg_idx + @num_opt_pargs)
- }
- end
-
- # Choose how many kwargs to use, and how many are optional
- num_kwargs = rand(10)
- @kwargs = (0...num_kwargs).map do |i|
- {
- :name => "k#{i}",
- :optional => rand() < 0.5
- }
- end
-
- # Choose whether to have rest parameters or not
- @has_rest = @num_opt_pargs == 0 && rand() < 0.5
- @has_kwrest = rand() < 0.25
-
- # Choose whether to have a named block parameter or not
- @has_block_param = rand() < 0.25
- end
-
- # Sample/generate a random set of arguments corresponding to the parameters
- def sample_args()
- # Choose how many positional args to pass
- num_pargs_passed = rand(@num_pargs_req..@pargs.size)
-
- # How many optional arguments will be filled
- opt_pargs_filled = num_pargs_passed - @num_pargs_req
-
- @pargs.each_with_index do |parg, i|
- if parg[:optional]
- parg[:default] = rand(100)
- end
-
- if !parg[:optional] || i < @opt_parg_idx + opt_pargs_filled
- parg[:argval] = rand(100)
- end
- end
-
- @kwargs.each_with_index do |kwarg, i|
- if kwarg[:optional]
- kwarg[:default] = rand(100)
- end
-
- if !kwarg[:optional] || rand() < 0.5
- kwarg[:argval] = rand(100)
- end
- end
-
- # Randomly pass a block or not
- @block_arg = nil
- if rand() < 0.5
- @block_arg = rand(100)
- end
- end
-
- # Compute the expected checksum of arguments ahead of time
- def compute_checksum()
- checksum = 0
-
- @pargs.each_with_index do |arg, i|
- value = (arg.key? :argval)? arg[:argval]:arg[:default]
- checksum += (i+1) * arg_val(value)
- end
-
- @kwargs.each_with_index do |arg, i|
- value = (arg.key? :argval)? arg[:argval]:arg[:default]
- checksum += (i+1) * arg_val(value)
- end
-
- if @block_arg
- if @has_block_param
- checksum += arg_val(@block_arg)
- end
-
- checksum += arg_val(@block_arg)
- end
-
- checksum
- end
-
- # Generate code for the method signature and method body
- def gen_method_str()
- m_str = "def m("
-
- @pargs.each do |arg|
- if !m_str.end_with?("(")
- m_str += ", "
- end
-
- m_str += arg[:name]
-
- # If this has a default value
- if arg[:optional]
- m_str += " = #{arg[:default]}"
- end
- end
-
- if @has_rest
- if !m_str.end_with?("(")
- m_str += ", "
- end
- m_str += "*rest"
- end
-
- @kwargs.each do |arg|
- if !m_str.end_with?("(")
- m_str += ", "
- end
-
- m_str += "#{arg[:name]}:"
-
- # If this has a default value
- if arg[:optional]
- m_str += " #{arg[:default]}"
- end
- end
-
- if @has_kwrest
- if !m_str.end_with?("(")
- m_str += ", "
- end
- m_str += "**kwrest"
- end
-
- if @has_block_param
- if !m_str.end_with?("(")
- m_str += ", "
- end
-
- m_str += "&block"
- end
-
- m_str += ")\n"
-
- # Add some useless locals
- rand(0...16).times do |i|
- m_str += "local#{i} = #{i}\n"
- end
-
- # Add some useless if statements
- @pargs.each_with_index do |arg, i|
- if rand() < 50
- m_str += "if #{arg[:name]} > 4; end\n"
- end
- end
-
- m_str += "checksum = 0\n"
-
- @pargs.each_with_index do |arg, i|
- m_str += "checksum += #{i+1} * arg_val(#{arg[:name]})\n"
- end
-
- @kwargs.each_with_index do |arg, i|
- m_str += "checksum += #{i+1} * arg_val(#{arg[:name]})\n"
- end
-
- if @has_block_param
- m_str += "if block; r = block.call; checksum += arg_val(r); end\n"
- end
-
- m_str += "if block_given?; r = yield; checksum += arg_val(r); end\n"
-
- if @has_rest
- m_str += "raise 'rest is not array' unless rest.kind_of?(Array)\n"
- m_str += "raise 'rest size not integer' unless rest.size.kind_of?(Integer)\n"
- end
-
- if @has_kwrest
- m_str += "raise 'kwrest is not a hash' unless kwrest.kind_of?(Hash)\n"
- m_str += "raise 'kwrest size not integer' unless kwrest.size.kind_of?(Integer)\n"
- end
-
- m_str += "checksum\n"
- m_str += "end"
-
- m_str
- end
-
- # Generate code to call into the method and pass the arguments
- def gen_call_str()
- c_str = "m("
-
- @pargs.each_with_index do |arg, i|
- if !arg.key? :argval
- next
- end
-
- if !c_str.end_with?("(")
- c_str += ", "
- end
-
- c_str += "#{arg[:argval]}"
- end
-
- @kwargs.each_with_index do |arg, i|
- if !arg.key? :argval
- next
- end
-
- if !c_str.end_with?("(")
- c_str += ", "
- end
-
- c_str += "#{arg[:name]}: #{arg[:argval]}"
- end
-
- c_str += ")"
-
- # Randomly pass a block or not
- if @block_arg
- c_str += " { #{@block_arg} }"
- end
-
- c_str
- end
-end
-
-iseqs_compiled_start = RubyVM::YJIT.runtime_stats[:compiled_iseq_entry]
-start_time = Time.now.to_f
-
-num_iters.times do |i|
- puts "Iteration #{i}"
-
- lst = ParamList.new()
- m_str = lst.gen_method_str()
- c_str = lst.gen_call_str()
- checksum = lst.compute_checksum()
-
- f = Object.new
-
- # Define the method on f
- puts "Defining"
- p m_str
- f.instance_eval(m_str)
- #puts RubyVM::InstructionSequence.disasm(f.method(:m))
- #exit 0
-
- puts "Calling"
- c_str = "f.#{c_str}"
- p c_str
- r = eval(c_str)
- puts "checksum=#{r}"
-
- if r != checksum
- raise "return value #{r} doesn't match checksum #{checksum}"
- end
-
- puts ""
-end
-
-# Make sure that YJIT actually compiled the tests we ran
-# Should be run with --yjit-call-threshold=1
-iseqs_compiled_end = RubyVM::YJIT.runtime_stats[:compiled_iseq_entry]
-if iseqs_compiled_end - iseqs_compiled_start < num_iters
- raise "YJIT did not compile enough ISEQs"
-end
-
-puts "Code region size: #{ format_number(0, RubyVM::YJIT.runtime_stats[:code_region_size]) }"
-
-end_time = Time.now.to_f
-itrs_per_sec = num_iters / (end_time - start_time)
-itrs_per_hour = 3600 * itrs_per_sec
-puts "#{'%.1f' % itrs_per_sec} iterations/s"
-puts "#{format_number(0, itrs_per_hour.round)} iterations/hour"
diff --git a/misc/call_fuzzer.sh b/misc/call_fuzzer.sh
deleted file mode 100755
index cf4ec76fe8..0000000000
--- a/misc/call_fuzzer.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-# Stop at first error
-set -e
-
-# TODO
-# TODO: boost --num-iters to 1M+ for actual test
-# TODO
-export NUM_ITERS=25000
-
-# Enable code GC so we don't stop compiling when we hit the code size limit
-ruby --yjit-call-threshold=1 --yjit-code-gc misc/call_fuzzer.rb --num-iters=$NUM_ITERS
-
-# Do another pass with --verify-ctx
-ruby --yjit-call-threshold=1 --yjit-code-gc --yjit-verify-ctx misc/call_fuzzer.rb --num-iters=$NUM_ITERS
diff --git a/misc/expand_tabs.rb b/misc/expand_tabs.rb
deleted file mode 100755
index a94eea5046..0000000000
--- a/misc/expand_tabs.rb
+++ /dev/null
@@ -1,208 +0,0 @@
-#!/usr/bin/env ruby --disable-gems
-# Add the following line to your `.git/hooks/pre-commit`:
-#
-# $ ruby --disable-gems misc/expand_tabs.rb
-#
-
-require 'shellwords'
-require 'tmpdir'
-ENV['LC_ALL'] = 'C'
-
-class Git
- def initialize(oldrev, newrev)
- @oldrev = oldrev
- @newrev = newrev
- end
-
- # ["foo/bar.c", "baz.h", ...]
- def updated_paths
- with_clean_env do
- IO.popen(['git', 'diff', '--cached', '--name-only', @newrev], &:readlines).each(&:chomp!)
- end
- end
-
- # [0, 1, 4, ...]
- def updated_lines(file)
- lines = []
- revs_pattern = ("0"*40) + " "
- with_clean_env { IO.popen(['git', 'blame', '-l', '--', file], &:readlines) }.each_with_index do |line, index|
- if line.b.start_with?(revs_pattern)
- lines << index
- end
- end
- lines
- end
-
- def add(file)
- git('add', file)
- end
-
- def toplevel
- IO.popen(['git', 'rev-parse', '--show-toplevel'], &:read).chomp
- end
-
- private
-
- def git(*args)
- cmd = ['git', *args].shelljoin
- unless with_clean_env { system(cmd) }
- abort "Failed to run: #{cmd}"
- end
- end
-
- def with_clean_env
- git_dir = ENV.delete('GIT_DIR') # this overcomes '-C' or pwd
- yield
- ensure
- ENV['GIT_DIR'] = git_dir if git_dir
- end
-end
-
-DEFAULT_GEM_LIBS = %w[
- abbrev
- base64
- benchmark
- bundler
- cmath
- csv
- debug
- delegate
- did_you_mean
- drb
- english
- erb
- fileutils
- find
- forwardable
- getoptlong
- ipaddr
- irb
- logger
- mutex_m
- net-http
- net-protocol
- observer
- open3
- open-uri
- optparse
- ostruct
- pp
- prettyprint
- prime
- pstore
- rdoc
- readline
- reline
- resolv
- resolv-replace
- rexml
- rinda
- rss
- rubygems
- scanf
- securerandom
- set
- shellwords
- singleton
- tempfile
- thwait
- time
- timeout
- tmpdir
- un
- tsort
- uri
- weakref
- yaml
-]
-
-DEFAULT_GEM_EXTS = %w[
- bigdecimal
- cgi
- date
- digest
- etc
- fcntl
- fiddle
- io-console
- io-nonblock
- io-wait
- json
- nkf
- openssl
- pathname
- psych
- racc
- readline-ext
- stringio
- strscan
- syslog
- win32ole
- zlib
-]
-
-EXPANDTAB_IGNORED_FILES = [
- # default gems whose master is GitHub
- %r{\Abin/(?!erb)\w+\z},
- *DEFAULT_GEM_LIBS.flat_map { |lib|
- [
- %r{\Alib/#{lib}/},
- %r{\Alib/#{lib}\.gemspec\z},
- %r{\Alib/#{lib}\.rb\z},
- %r{\Atest/#{lib}/},
- ]
- },
- *DEFAULT_GEM_EXTS.flat_map { |ext|
- [
- %r{\Aext/#{ext}/},
- %r{\Atest/#{ext}/},
- ]
- },
-
- # vendoring (ccan)
- %r{\Accan/},
-
- # vendoring (onigmo)
- %r{\Aenc/},
- %r{\Ainclude/ruby/onigmo\.h\z},
- %r{\Areg.+\.(c|h)\z},
-
- # explicit or implicit `c-file-style: "linux"`
- %r{\Aaddr2line\.c\z},
- %r{\Amissing/},
- %r{\Astrftime\.c\z},
- %r{\Avsnprintf\.c\z},
-]
-
-git = Git.new('HEAD^', 'HEAD')
-
-Dir.chdir(git.toplevel) do
- paths = git.updated_paths
- paths.select! {|f|
- (f.end_with?('.c') || f.end_with?('.h') || f == 'insns.def') && EXPANDTAB_IGNORED_FILES.all? { |re| !f.match(re) }
- }
- files = paths.select {|n| File.file?(n)}
- exit if files.empty?
-
- files.each do |f|
- src = File.binread(f) rescue next
-
- expanded = false
- updated_lines = git.updated_lines(f)
- unless updated_lines.empty?
- src.gsub!(/^.*$/).with_index do |line, lineno|
- if updated_lines.include?(lineno) && line.start_with?("\t") # last-committed line with hard tabs
- expanded = true
- line.sub(/\A\t+/) { |tabs| ' ' * (8 * tabs.length) }
- else
- line
- end
- end
- end
-
- if expanded
- File.binwrite(f, src)
- git.add(f)
- end
- end
-end
diff --git a/misc/gdb.py b/misc/gdb.py
deleted file mode 100644
index 6034a389bb..0000000000
--- a/misc/gdb.py
+++ /dev/null
@@ -1,181 +0,0 @@
-import argparse
-import textwrap
-
-# usage: [-h] [-a | --all | --no-all] [-s STACK_SIZE] [uplevel]
-#
-# Dump a control frame
-#
-# positional arguments:
-# uplevel CFP offset from the stack top
-#
-# options:
-# -h, --help show this help message and exit
-# -a, --all, --no-all dump all frames
-# -s STACK_SIZE, --stack-size STACK_SIZE
-# override stack_size (useful for JIT frames)
-class CFP(gdb.Command):
- FRAME_MAGICS = [
- # frame types
- 'VM_FRAME_MAGIC_METHOD',
- 'VM_FRAME_MAGIC_BLOCK',
- 'VM_FRAME_MAGIC_CLASS',
- 'VM_FRAME_MAGIC_TOP',
- 'VM_FRAME_MAGIC_CFUNC',
- 'VM_FRAME_MAGIC_IFUNC',
- 'VM_FRAME_MAGIC_EVAL',
- 'VM_FRAME_MAGIC_RESCUE',
- 'VM_FRAME_MAGIC_DUMMY',
- ]
- FRAME_FLAGS = [
- # frame flag
- 'VM_FRAME_FLAG_FINISH',
- 'VM_FRAME_FLAG_BMETHOD',
- 'VM_FRAME_FLAG_CFRAME',
- 'VM_FRAME_FLAG_LAMBDA',
- 'VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM',
- 'VM_FRAME_FLAG_CFRAME_KW',
- 'VM_FRAME_FLAG_PASSED',
- # env flag
- 'VM_ENV_FLAG_LOCAL',
- 'VM_ENV_FLAG_ESCAPED',
- 'VM_ENV_FLAG_WB_REQUIRED',
- 'VM_ENV_FLAG_ISOLATED',
- ]
-
- def __init__(self):
- super(CFP, self).__init__('cfp', gdb.COMMAND_USER)
-
- self.parser = argparse.ArgumentParser(description='Dump a control frame')
- self.parser.add_argument('uplevel', type=int, nargs='?', default=0, help='CFP offset from the stack top')
- self.parser.add_argument('-a', '--all', action=argparse.BooleanOptionalAction, help='dump all frames')
- self.parser.add_argument('-s', '--stack-size', type=int, help='override stack_size (useful for JIT frames)')
-
- def invoke(self, args, from_tty):
- try:
- args = self.parser.parse_args(args.split())
- except SystemExit:
- return
- cfp = f'(ruby_current_ec->cfp + ({args.uplevel}))'
- end_cfp = self.get_int('ruby_current_ec->vm_stack + ruby_current_ec->vm_stack_size')
- cfp_index = int((end_cfp - self.get_int(cfp) - 1) / self.get_int('sizeof(rb_control_frame_t)'))
-
- if args.all:
- cfp_count = int((end_cfp - self.get_int('ruby_current_ec->cfp')) / self.get_int('sizeof(rb_control_frame_t)')) - 1 # exclude dummy CFP
- for i in range(cfp_count):
- print('-' * 80)
- self.invoke(str(cfp_count - i - 1), from_tty)
- return
-
- print('CFP (addr=0x{:x}, index={}):'.format(self.get_int(cfp), cfp_index))
- gdb.execute(f'p *({cfp})')
- print()
-
- if self.get_int(f'{cfp}->iseq'):
- local_size = self.get_int(f'{cfp}->iseq->body->local_table_size - {cfp}->iseq->body->param.size')
- param_size = self.get_int(f'{cfp}->iseq->body->param.size')
-
- if local_size:
- print(f'Params (size={param_size}):')
- for i in range(-3 - local_size - param_size, -3 - local_size):
- self.print_stack(cfp, i, self.rp(cfp, i))
- print()
-
- if param_size:
- print(f'Locals (size={local_size}):')
- for i in range(-3 - local_size, -3):
- self.print_stack(cfp, i, self.rp(cfp, i))
- print()
-
- print('Env:')
- self.print_env(cfp, -3, self.rp_env(cfp, -3))
- self.print_env(cfp, -2, self.specval(cfp, -2))
- self.print_env(cfp, -1, self.frame_types(cfp, -1))
- print()
-
- # We can't calculate BP for the first frame.
- # vm_base_ptr doesn't work for C frames either.
- if cfp_index > 0 and self.get_int(f'{cfp}->iseq'):
- if args.stack_size is not None:
- stack_size = args.stack_size
- else:
- stack_size = int((self.get_int(f'{cfp}->sp') - self.get_int(f'vm_base_ptr({cfp})')) / 8)
- print(f'Stack (size={stack_size}):')
- for i in range(0, stack_size):
- self.print_stack(cfp, i, self.rp(cfp, i))
- print(self.regs(cfp, stack_size))
-
- def print_env(self, cfp, bp_index, content):
- ep_index = bp_index + 1
- address = self.get_int(f'((rb_control_frame_t *){cfp})->ep + {ep_index}')
- value = self.get_env(cfp, bp_index)
- regs = self.regs(cfp, bp_index)
- if content:
- content = textwrap.indent(content, ' ' * 3).lstrip() # Leave the regs column empty
- content = f'{content} '
- print('{:2} 0x{:x} [{}] {}(0x{:x})'.format(regs, address, bp_index, content, value))
-
- def print_stack(self, cfp, bp_index, content):
- address = self.get_int(f'vm_base_ptr({cfp}) + {bp_index}')
- value = self.get_value(cfp, bp_index)
- regs = self.regs(cfp, bp_index)
- if content:
- content = textwrap.indent(content, ' ' * 3).lstrip() # Leave the regs column empty
- content = f'{content} '
- print('{:2} 0x{:x} [{}] {}(0x{:x})'.format(regs, address, bp_index, content, value))
-
- def regs(self, cfp, bp_index):
- address = self.get_int(f'vm_base_ptr({cfp}) + {bp_index}')
- regs = []
- for reg, field in { 'EP': 'ep', 'SP': 'sp' }.items():
- if address == self.get_int(f'{cfp}->{field}'):
- regs.append(reg)
- return ' '.join(regs)
-
- def rp(self, cfp, bp_index):
- value = self.get_value(cfp, bp_index)
- return self.get_string(f'rp {value}').rstrip()
-
- def rp_env(self, cfp, bp_index):
- value = self.get_env(cfp, bp_index)
- return self.get_string(f'rp {value}').rstrip()
-
- # specval: block_handler or previous EP
- def specval(self, cfp, bp_index):
- value = self.get_env(cfp, bp_index)
- if value == 0:
- return 'VM_BLOCK_HANDLER_NONE'
- if value == self.get_int('rb_block_param_proxy'):
- return 'rb_block_param_proxy'
- return ''
-
- def frame_types(self, cfp, bp_index):
- types = []
- value = self.get_env(cfp, bp_index)
-
- magic_mask = self.get_int('VM_FRAME_MAGIC_MASK')
- for magic in self.FRAME_MAGICS:
- magic_value = self.get_int(magic)
- if value & magic_mask == magic_value:
- types.append(magic)
-
- for flag in self.FRAME_FLAGS:
- flag_value = self.get_int(flag)
- if value & flag_value:
- types.append(flag)
-
- return ' | '.join(types)
-
- def get_env(self, cfp, bp_index):
- ep_index = bp_index + 1
- return self.get_int(f'((rb_control_frame_t *){cfp})->ep[{ep_index}]')
-
- def get_value(self, cfp, bp_index):
- return self.get_int(f'vm_base_ptr({cfp})[{bp_index}]')
-
- def get_int(self, expr):
- return int(self.get_string(f'printf "%ld", ({expr})'))
-
- def get_string(self, expr):
- return gdb.execute(expr, to_string=True)
-
-CFP()
diff --git a/misc/inf-ruby.el b/misc/inf-ruby.el
new file mode 100644
index 0000000000..b3f4f10267
--- /dev/null
+++ b/misc/inf-ruby.el
@@ -0,0 +1,418 @@
+;;; -*-Emacs-Lisp-*-
+;;;
+;;; $Id$
+;;; $Author$
+;;;
+;;; Inferior Ruby Mode - ruby process in a buffer.
+;;; adapted from cmuscheme.el
+;;;
+;;; Usage:
+;;;
+;;; (0) check ruby-program-name variable that can run your environment.
+;;;
+;;; (1) modify .emacs to use ruby-mode
+;;; for example :
+;;;
+;;; (autoload 'ruby-mode "ruby-mode"
+;;; "Mode for editing ruby source files" t)
+;;; (setq auto-mode-alist
+;;; (append '(("\\.rb$" . ruby-mode)) auto-mode-alist))
+;;; (setq interpreter-mode-alist (append '(("ruby" . ruby-mode))
+;;; interpreter-mode-alist))
+;;;
+;;; (2) set to load inf-ruby and set inf-ruby key definition in ruby-mode.
+;;;
+;;; (autoload 'run-ruby "inf-ruby"
+;;; "Run an inferior Ruby process")
+;;; (autoload 'inf-ruby-keys "inf-ruby"
+;;; "Set local key defs for inf-ruby in ruby-mode")
+;;; (add-hook 'ruby-mode-hook
+;;; '(lambda ()
+;;; (inf-ruby-keys)
+;;; ))
+;;;
+;;; HISTORY
+;;; senda - 8 Apr 1998: Created.
+;;; $Log$
+;;; Revision 1.7 2004/07/27 08:11:36 matz
+;;; * eval.c (rb_eval): copy on write for argument local variable
+;;; assignment.
+;;;
+;;; * eval.c (assign): ditto.
+;;;
+;;; * eval.c (rb_call0): update ruby_frame->argv with the default
+;;; value used for the optional arguments.
+;;;
+;;; * object.c (Init_Object): "===" calls rb_obj_equal() directly.
+;;; [ruby-list:39937]
+;;;
+;;; Revision 1.6 2002/09/07 14:35:46 nobu
+;;; * misc/inf-ruby.el (inferior-ruby-error-regexp-alist): regexp
+;;; alist for error message from ruby.
+;;;
+;;; * misc/inf-ruby.el (inferior-ruby-mode): fixed for Emacs.
+;;;
+;;; * misc/inf-ruby.el (ruby-send-region): compilation-parse-errors
+;;; doesn't parse first line, so insert separators before each
+;;; evaluations.
+;;;
+;;; Revision 1.5 2002/08/19 10:05:47 nobu
+;;; * misc/inf-ruby.el (inf-ruby-keys): ruby-send-definition
+;;; conflicted with ruby-insert-end.
+;;;
+;;; * misc/inf-ruby.el (inferior-ruby-mode): compilation-minor-mode.
+;;;
+;;; * misc/inf-ruby.el (ruby-send-region): send as here document to
+;;; adjust source file/line. [ruby-talk:47113], [ruby-dev:17965]
+;;;
+;;; * misc/inf-ruby.el (ruby-send-terminator): added to make unique
+;;; terminator.
+;;;
+;;; Revision 1.4 2002/01/29 07:16:09 matz
+;;; * file.c (rb_stat_rdev_major): added. [new]
+;;;
+;;; * file.c (rb_stat_rdev_minor): added. [new]
+;;;
+;;; * file.c (rb_stat_inspect): print mode in octal.
+;;;
+;;; Revision 1.3 1999/12/01 09:24:18 matz
+;;; 19991201
+;;;
+;;; Revision 1.2 1999/08/13 05:45:18 matz
+;;; 1.4.0
+;;;
+;;; Revision 1.1.1.1.2.1 1999/07/15 07:59:59 matz
+;;; 990715
+;;;
+;;; Revision 1.1.1.1 1999/01/20 04:59:36 matz
+;;; ruby 1.3 cycle
+;;;
+;;; Revision 1.1.2.1 1998/12/16 07:30:36 matz
+;;; first public release of 1.1d (pre1.2) series
+;;;
+;;; Revision 1.4 1998/05/20 02:45:58 senda
+;;; default program to irb
+;;;
+;;; Revision 1.3 1998/04/10 04:11:30 senda
+;;; modification by Matsumoto san (1.1b9_09)
+;;; remove-in-string defined
+;;; global variable :
+;;; inferior-ruby-first-prompt-pattern
+;;; inferior-ruby-prompt-pattern
+;;; defined
+;;;
+;;; Revision 1.2 1998/04/09 07:53:42 senda
+;;; remove M-C-x in inferior-ruby-mode
+;;;
+;;; Revision 1.1 1998/04/09 07:28:36 senda
+;;; Initial revision
+;;;
+;;;
+
+(require 'comint)
+(require 'compile)
+(require 'ruby-mode)
+
+;;
+;; you may change these variables
+;;
+;(defvar ruby-program-name "rbc --noreadline"
+; "*Program invoked by the run-ruby command")
+;
+;(defvar inferior-ruby-first-prompt-pattern "^rbc0> *"
+; "first prompt regex pattern of ruby interpreter.")
+;
+;(defvar inferior-ruby-prompt-pattern "^\\(rbc.[>*\"'] *\\)+"
+; "prompt regex pattern of ruby interpreter.")
+
+;;;; for irb
+(defvar ruby-program-name "irb --inf-ruby-mode"
+ "*Program invoked by the run-ruby command")
+
+(defvar inferior-ruby-first-prompt-pattern "^irb(.*)[0-9:]+0> *"
+ "first prompt regex pattern of ruby interpreter.")
+
+(defvar inferior-ruby-prompt-pattern "^\\(irb(.*)[0-9:]+[>*\"'] *\\)+"
+ "prompt regex pattern of ruby interpreter.")
+
+;;
+;; mode variables
+;;
+(defvar inferior-ruby-mode-hook nil
+ "*Hook for customising inferior-ruby mode.")
+(defvar inferior-ruby-mode-map nil
+ "*Mode map for inferior-ruby-mode")
+
+(defconst inferior-ruby-error-regexp-alist
+ '(("SyntaxError: compile error\n^\\([^\(].*\\):\\([1-9][0-9]*\\):" 1 2)
+ ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2)))
+
+(cond ((not inferior-ruby-mode-map)
+ (setq inferior-ruby-mode-map
+ (copy-keymap comint-mode-map))
+; (define-key inferior-ruby-mode-map "\M-\C-x" ;gnu convention
+; 'ruby-send-definition)
+; (define-key inferior-ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
+ (define-key inferior-ruby-mode-map "\C-c\C-l" 'ruby-load-file)
+))
+
+;;;###autoload
+(defun inf-ruby-keys ()
+ "Set local key defs for inf-ruby in ruby-mode"
+ (define-key ruby-mode-map "\M-\C-x" 'ruby-send-definition)
+; (define-key ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
+ (define-key ruby-mode-map "\C-c\C-b" 'ruby-send-block)
+ (define-key ruby-mode-map "\C-c\M-b" 'ruby-send-block-and-go)
+ (define-key ruby-mode-map "\C-c\C-x" 'ruby-send-definition)
+ (define-key ruby-mode-map "\C-c\M-x" 'ruby-send-definition-and-go)
+ (define-key ruby-mode-map "\C-c\C-r" 'ruby-send-region)
+ (define-key ruby-mode-map "\C-c\M-r" 'ruby-send-region-and-go)
+ (define-key ruby-mode-map "\C-c\C-z" 'switch-to-ruby)
+ (define-key ruby-mode-map "\C-c\C-l" 'ruby-load-file)
+ (define-key ruby-mode-map "\C-c\C-s" 'run-ruby)
+)
+
+(defvar ruby-buffer nil "current ruby (actually irb) process buffer.")
+
+(defun inferior-ruby-mode ()
+ "Major mode for interacting with an inferior ruby (irb) process.
+
+The following commands are available:
+\\{inferior-ruby-mode-map}
+
+A ruby process can be fired up with M-x run-ruby.
+
+Customisation: Entry to this mode runs the hooks on comint-mode-hook and
+inferior-ruby-mode-hook (in that order).
+
+You can send text to the inferior ruby process from other buffers containing
+Ruby source.
+ switch-to-ruby switches the current buffer to the ruby process buffer.
+ ruby-send-definition sends the current definition to the ruby process.
+ ruby-send-region sends the current region to the ruby process.
+
+ ruby-send-definition-and-go, ruby-send-region-and-go,
+ switch to the ruby process buffer after sending their text.
+For information on running multiple processes in multiple buffers, see
+documentation for variable ruby-buffer.
+
+Commands:
+Return after the end of the process' output sends the text from the
+ end of process to point.
+Return before the end of the process' output copies the sexp ending at point
+ to the end of the process' output, and sends it.
+Delete converts tabs to spaces as it moves back.
+Tab indents for ruby; with argument, shifts rest
+ of expression rigidly with the current line.
+C-M-q does Tab on each line starting within following expression.
+Paragraphs are separated only by blank lines. # start comments.
+If you accidentally suspend your process, use \\[comint-continue-subjob]
+to continue it."
+ (interactive)
+ (comint-mode)
+ ;; Customise in inferior-ruby-mode-hook
+ ;(setq comint-prompt-regexp "^[^>\n]*>+ *")
+ (setq comint-prompt-regexp inferior-ruby-prompt-pattern)
+ ;;(scheme-mode-variables)
+ (ruby-mode-variables)
+ (setq major-mode 'inferior-ruby-mode)
+ (setq mode-name "Inferior Ruby")
+ (setq mode-line-process '(":%s"))
+ (use-local-map inferior-ruby-mode-map)
+ (setq comint-input-filter (function ruby-input-filter))
+ (setq comint-get-old-input (function ruby-get-old-input))
+ (make-local-variable 'compilation-error-regexp-alist)
+ (setq compilation-error-regexp-alist inferior-ruby-error-regexp-alist)
+ (compilation-shell-minor-mode t)
+ (run-hooks 'inferior-ruby-mode-hook))
+
+(defvar inferior-ruby-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
+ "*Input matching this regexp are not saved on the history list.
+Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")
+
+(defun ruby-input-filter (str)
+ "Don't save anything matching inferior-ruby-filter-regexp"
+ (not (string-match inferior-ruby-filter-regexp str)))
+
+;; adapted from replace-in-string in XEmacs (subr.el)
+(defun remove-in-string (str regexp)
+ "Remove all matches in STR for REGEXP and returns the new string."
+ (let ((rtn-str "") (start 0) match prev-start)
+ (while (setq match (string-match regexp str start))
+ (setq prev-start start
+ start (match-end 0)
+ rtn-str (concat rtn-str (substring str prev-start match))))
+ (concat rtn-str (substring str start))))
+
+(defun ruby-get-old-input ()
+ "Snarf the sexp ending at point"
+ (save-excursion
+ (let ((end (point)))
+ (re-search-backward inferior-ruby-first-prompt-pattern)
+ (remove-in-string (buffer-substring (point) end)
+ inferior-ruby-prompt-pattern)
+ )))
+
+(defun ruby-args-to-list (string)
+ (let ((where (string-match "[ \t]" string)))
+ (cond ((null where) (list string))
+ ((not (= where 0))
+ (cons (substring string 0 where)
+ (ruby-args-to-list (substring string (+ 1 where)
+ (length string)))))
+ (t (let ((pos (string-match "[^ \t]" string)))
+ (if (null pos)
+ nil
+ (ruby-args-to-list (substring string pos
+ (length string)))))))))
+
+;;;###autoload
+(defun run-ruby (cmd)
+ "Run an inferior Ruby process, input and output via buffer *ruby*.
+If there is a process already running in `*ruby*', switch to that buffer.
+With argument, allows you to edit the command line (default is value
+of `ruby-program-name'). Runs the hooks `inferior-ruby-mode-hook'
+\(after the `comint-mode-hook' is run).
+\(Type \\[describe-mode] in the process buffer for a list of commands.)"
+
+ (interactive (list (if current-prefix-arg
+ (read-string "Run Ruby: " ruby-program-name)
+ ruby-program-name)))
+ (if (not (comint-check-proc "*ruby*"))
+ (let ((cmdlist (ruby-args-to-list cmd)))
+ (set-buffer (apply 'make-comint "ruby" (car cmdlist)
+ nil (cdr cmdlist)))
+ (inferior-ruby-mode)))
+ (setq ruby-program-name cmd)
+ (setq ruby-buffer "*ruby*")
+ (pop-to-buffer "*ruby*"))
+
+(defconst ruby-send-terminator "--inf-ruby-%x-%d-%d-%d--"
+ "Template for irb here document terminator.
+Must not contain ruby meta characters.")
+
+(defconst ruby-eval-separator "")
+
+(defun ruby-send-region (start end)
+ "Send the current region to the inferior Ruby process."
+ (interactive "r")
+ (let (term (file (buffer-file-name)) line)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char start)
+ (setq line (+ start (forward-line (- start)) 1))
+ (goto-char start)
+ (while (progn
+ (setq term (apply 'format ruby-send-terminator (random) (current-time)))
+ (re-search-forward (concat "^" (regexp-quote term) "$") end t)))))
+ ;; compilation-parse-errors parses from second line.
+ (save-excursion
+ (let ((m (process-mark (ruby-proc))))
+ (set-buffer (marker-buffer m))
+ (goto-char m)
+ (insert ruby-eval-separator "\n")
+ (set-marker m (point))))
+ (comint-send-string (ruby-proc) (format "eval <<'%s', nil, %S, %d\n" term file line))
+ (comint-send-region (ruby-proc) start end)
+ (comint-send-string (ruby-proc) (concat "\n" term "\n"))))
+
+(defun ruby-send-definition ()
+ "Send the current definition to the inferior Ruby process."
+ (interactive)
+ (save-excursion
+ (ruby-end-of-defun)
+ (let ((end (point)))
+ (ruby-beginning-of-defun)
+ (ruby-send-region (point) end))))
+
+;(defun ruby-send-last-sexp ()
+; "Send the previous sexp to the inferior Ruby process."
+; (interactive)
+; (ruby-send-region (save-excursion (backward-sexp) (point)) (point)))
+
+(defun ruby-send-block ()
+ "Send the current block to the inferior Ruby process."
+ (interactive)
+ (save-excursion
+ (ruby-end-of-block)
+ (end-of-line)
+ (let ((end (point)))
+ (ruby-beginning-of-block)
+ (ruby-send-region (point) end))))
+
+(defun switch-to-ruby (eob-p)
+ "Switch to the ruby process buffer.
+With argument, positions cursor at end of buffer."
+ (interactive "P")
+ (if (get-buffer ruby-buffer)
+ (pop-to-buffer ruby-buffer)
+ (error "No current process buffer. See variable ruby-buffer."))
+ (cond (eob-p
+ (push-mark)
+ (goto-char (point-max)))))
+
+(defun ruby-send-region-and-go (start end)
+ "Send the current region to the inferior Ruby process.
+Then switch to the process buffer."
+ (interactive "r")
+ (ruby-send-region start end)
+ (switch-to-ruby t))
+
+(defun ruby-send-definition-and-go ()
+ "Send the current definition to the inferior Ruby.
+Then switch to the process buffer."
+ (interactive)
+ (ruby-send-definition)
+ (switch-to-ruby t))
+
+(defun ruby-send-block-and-go ()
+ "Send the current block to the inferior Ruby.
+Then switch to the process buffer."
+ (interactive)
+ (ruby-send-block)
+ (switch-to-ruby t))
+
+(defvar ruby-source-modes '(ruby-mode)
+ "*Used to determine if a buffer contains Ruby source code.
+If it's loaded into a buffer that is in one of these major modes, it's
+considered a ruby source file by ruby-load-file.
+Used by these commands to determine defaults.")
+
+(defvar ruby-prev-l/c-dir/file nil
+ "Caches the last (directory . file) pair.
+Caches the last pair used in the last ruby-load-file command.
+Used for determining the default in the
+next one.")
+
+(defun ruby-load-file (file-name)
+ "Load a Ruby file into the inferior Ruby process."
+ (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file
+ ruby-source-modes t)) ; T because LOAD
+ ; needs an exact name
+ (comint-check-source file-name) ; Check to see if buffer needs saved.
+ (setq ruby-prev-l/c-dir/file (cons (file-name-directory file-name)
+ (file-name-nondirectory file-name)))
+ (comint-send-string (ruby-proc) (concat "(load \""
+ file-name
+ "\"\)\n")))
+
+(defun ruby-proc ()
+ "Returns the current ruby process. See variable ruby-buffer."
+ (let ((proc (get-buffer-process (if (eq major-mode 'inferior-ruby-mode)
+ (current-buffer)
+ ruby-buffer))))
+ (or proc
+ (error "No current process. See variable ruby-buffer"))))
+
+;;; Do the user's customisation...
+
+(defvar inf-ruby-load-hook nil
+ "This hook is run when inf-ruby is loaded in.
+This is a good place to put keybindings.")
+
+(run-hooks 'inf-ruby-load-hook)
+
+(provide 'inf-ruby)
+
+;;; inf-ruby.el ends here
diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py
index 0707bb42d3..e954d1be00 100755
--- a/misc/lldb_cruby.py
+++ b/misc/lldb_cruby.py
@@ -6,161 +6,10 @@
# Test: misc/test_lldb_cruby.rb
#
-from __future__ import print_function
import lldb
+import commands
import os
-import inspect
-import sys
import shlex
-import platform
-import glob
-
-from lldb_rb.constants import *
-
-# BEGIN FUNCTION STYLE DECLS
-# This will be refactored to use class style decls in the misc/commands dir
-class BackTrace:
- VM_FRAME_MAGIC_METHOD = 0x11110001
- VM_FRAME_MAGIC_BLOCK = 0x22220001
- VM_FRAME_MAGIC_CLASS = 0x33330001
- VM_FRAME_MAGIC_TOP = 0x44440001
- VM_FRAME_MAGIC_CFUNC = 0x55550001
- VM_FRAME_MAGIC_IFUNC = 0x66660001
- VM_FRAME_MAGIC_EVAL = 0x77770001
- VM_FRAME_MAGIC_RESCUE = 0x78880001
- VM_FRAME_MAGIC_DUMMY = 0x79990001
-
- VM_FRAME_MAGIC_MASK = 0x7fff0001
-
- VM_FRAME_MAGIC_NAME = {
- VM_FRAME_MAGIC_TOP: "TOP",
- VM_FRAME_MAGIC_METHOD: "METHOD",
- VM_FRAME_MAGIC_CLASS: "CLASS",
- VM_FRAME_MAGIC_BLOCK: "BLOCK",
- VM_FRAME_MAGIC_CFUNC: "CFUNC",
- VM_FRAME_MAGIC_IFUNC: "IFUNC",
- VM_FRAME_MAGIC_EVAL: "EVAL",
- VM_FRAME_MAGIC_RESCUE: "RESCUE",
- 0: "-----"
- }
-
- def __init__(self, debugger, command, result, internal_dict):
- self.debugger = debugger
- self.command = command
- self.result = result
-
- self.target = debugger.GetSelectedTarget()
- self.process = self.target.GetProcess()
- self.thread = self.process.GetSelectedThread()
- self.frame = self.thread.GetSelectedFrame()
- self.tRString = self.target.FindFirstType("struct RString").GetPointerType()
- self.tRArray = self.target.FindFirstType("struct RArray").GetPointerType()
-
- rb_cft_len = len("rb_control_frame_t")
- method_type_length = sorted(map(len, self.VM_FRAME_MAGIC_NAME.values()), reverse=True)[0]
- # cfp address, method type, function name
- self.fmt = "%%-%ds %%-%ds %%s" % (rb_cft_len, method_type_length)
-
- def vm_frame_magic(self, cfp):
- ep = cfp.GetValueForExpressionPath("->ep")
- frame_type = ep.GetChildAtIndex(0).GetValueAsUnsigned() & self.VM_FRAME_MAGIC_MASK
- return self.VM_FRAME_MAGIC_NAME.get(frame_type, "(none)")
-
- def rb_iseq_path_str(self, iseq):
- tRBasic = self.target.FindFirstType("struct RBasic").GetPointerType()
-
- pathobj = iseq.GetValueForExpressionPath("->body->location.pathobj")
- pathobj = pathobj.Cast(tRBasic)
- flags = pathobj.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
- flType = flags & RUBY_T_MASK
-
- if flType == RUBY_T_ARRAY:
- pathobj = pathobj.Cast(self.tRArray)
-
- if flags & RUBY_FL_USER1:
- len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5|RUBY_FL_USER6|RUBY_FL_USER7|RUBY_FL_USER8|RUBY_FL_USER9)) >> (RUBY_FL_USHIFT+3))
- ptr = pathobj.GetValueForExpressionPath("->as.ary")
- else:
- len = pathobj.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
- ptr = pathobj.GetValueForExpressionPath("->as.heap.ptr")
-
- pathobj = ptr.GetChildAtIndex(0)
-
- pathobj = pathobj.Cast(self.tRString)
- ptr, len = string2cstr(pathobj)
- err = lldb.SBError()
- path = self.target.process.ReadMemory(ptr, len, err)
- if err.Success():
- return path.decode("utf-8")
- else:
- return "unknown"
-
- def dump_iseq_frame(self, cfp, iseq):
- m = self.vm_frame_magic(cfp)
-
- if iseq.GetValueAsUnsigned():
- iseq_label = iseq.GetValueForExpressionPath("->body->location.label")
- path = self.rb_iseq_path_str(iseq)
- ptr, len = string2cstr(iseq_label.Cast(self.tRString))
-
- err = lldb.SBError()
- iseq_name = self.target.process.ReadMemory(ptr, len, err)
- if err.Success():
- iseq_name = iseq_name.decode("utf-8")
- else:
- iseq_name = "error!!"
-
- else:
- print("No iseq", file=self.result)
-
- print(self.fmt % (("%0#12x" % cfp.GetAddress().GetLoadAddress(self.target)), m, "%s %s" % (path, iseq_name)), file=self.result)
-
- def dump_cfunc_frame(self, cfp):
- print(self.fmt % ("%0#12x" % (cfp.GetAddress().GetLoadAddress(self.target)), "CFUNC", ""), file=self.result)
-
- def print_bt(self, ec):
- tRbExecutionContext_t = self.target.FindFirstType("rb_execution_context_t")
- ec = ec.Cast(tRbExecutionContext_t.GetPointerType())
- vm_stack = ec.GetValueForExpressionPath("->vm_stack")
- vm_stack_size = ec.GetValueForExpressionPath("->vm_stack_size")
-
- last_cfp_frame = ec.GetValueForExpressionPath("->cfp")
- cfp_type_p = last_cfp_frame.GetType()
-
- stack_top = vm_stack.GetValueAsUnsigned() + (
- vm_stack_size.GetValueAsUnsigned() * vm_stack.GetType().GetByteSize())
-
- cfp_frame_size = cfp_type_p.GetPointeeType().GetByteSize()
-
- start_cfp = stack_top
- # Skip dummy frames
- start_cfp -= cfp_frame_size
- start_cfp -= cfp_frame_size
-
- last_cfp = last_cfp_frame.GetValueAsUnsigned()
-
- size = ((start_cfp - last_cfp) / cfp_frame_size) + 1
-
- print(self.fmt % ("rb_control_frame_t", "TYPE", ""), file=self.result)
-
- curr_addr = start_cfp
-
- while curr_addr >= last_cfp:
- cfp = self.target.CreateValueFromAddress("cfp", lldb.SBAddress(curr_addr, self.target), cfp_type_p.GetPointeeType())
- ep = cfp.GetValueForExpressionPath("->ep")
- iseq = cfp.GetValueForExpressionPath("->iseq")
-
- frame_type = ep.GetChildAtIndex(0).GetValueAsUnsigned() & self.VM_FRAME_MAGIC_MASK
-
- if iseq.GetValueAsUnsigned():
- pc = cfp.GetValueForExpressionPath("->pc")
- if pc.GetValueAsUnsigned():
- self.dump_iseq_frame(cfp, iseq)
- else:
- if frame_type == self.VM_FRAME_MAGIC_CFUNC:
- self.dump_cfunc_frame(cfp)
-
- curr_addr -= cfp_frame_size
def lldb_init(debugger):
target = debugger.GetSelectedTarget()
@@ -169,18 +18,10 @@ def lldb_init(debugger):
value_types = []
g = globals()
-
- imemo_types = target.FindFirstType('enum imemo_type')
- enum_members = imemo_types.GetEnumMembers()
-
- for i in range(enum_members.GetSize()):
- member = enum_members.GetTypeEnumMemberAtIndex(i)
- g[member.GetName()] = member.GetValueAsUnsigned()
-
for enum in target.FindFirstGlobalVariable('ruby_dummy_gdb_enums'):
enum = enum.GetType()
members = enum.GetEnumMembers()
- for i in range(0, members.GetSize()):
+ for i in xrange(0, members.GetSize()):
member = members.GetTypeEnumMemberAtIndex(i)
name = member.GetName()
value = member.GetValueAsUnsigned()
@@ -192,21 +33,21 @@ def lldb_init(debugger):
def string2cstr(rstring):
"""Returns the pointer to the C-string in the given String object"""
- if rstring.TypeIsPointerType():
- rstring = rstring.Dereference()
flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned
if flags & RUBY_T_MASK != RUBY_T_STRING:
raise TypeError("not a string")
- clen = int(rstring.GetValueForExpressionPath(".len").value, 0)
if flags & RUBY_FL_USER1:
cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0)
+ clen = int(rstring.GetValueForExpressionPath(".as.heap.len").value, 0)
else:
- cptr = int(rstring.GetValueForExpressionPath(".as.embed.ary").location, 0)
+ cptr = int(rstring.GetValueForExpressionPath(".as.ary").value, 0)
+ clen = (flags & RSTRING_EMBED_LEN_MASK) >> RSTRING_EMBED_LEN_SHIFT
return cptr, clen
-def output_string(debugger, result, rstring):
+def output_string(ctx, rstring):
cptr, clen = string2cstr(rstring)
- append_expression(debugger, "*(const char (*)[%d])%0#x" % (clen, cptr), result)
+ expr = 'printf("%%.*s", (size_t)%d, (const char*)%d)' % (clen, cptr)
+ ctx.frame.EvaluateExpression(expr)
def fixnum_p(x):
return x & RUBY_FIXNUM_FLAG != 0
@@ -214,24 +55,7 @@ def fixnum_p(x):
def flonum_p(x):
return (x&RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG
-def static_sym_p(x):
- return (x&~(~0<<RUBY_SPECIAL_SHIFT)) == RUBY_SYMBOL_FLAG
-
-def append_command_output(debugger, command, result):
- output1 = result.GetOutput()
- debugger.GetCommandInterpreter().HandleCommand(command, result)
- output2 = result.GetOutput()
- result.Clear()
- result.write(output1)
- result.write(output2)
-
-def append_expression(debugger, expression, result):
- append_command_output(debugger, "expression " + expression, result)
-
def lldb_rp(debugger, command, result, internal_dict):
- if not ('RUBY_Qfalse' in globals()):
- lldb_init(debugger)
-
target = debugger.GetSelectedTarget()
process = target.GetProcess()
thread = process.GetSelectedThread()
@@ -242,198 +66,70 @@ def lldb_rp(debugger, command, result, internal_dict):
val = target.EvaluateExpression(command)
error = val.GetError()
if error.Fail():
- print(error, file=result)
+ print >> result, error
return
- lldb_inspect(debugger, target, result, val)
-
-def lldb_inspect(debugger, target, result, val):
num = val.GetValueAsSigned()
if num == RUBY_Qfalse:
- print('false', file=result)
+ print >> result, 'false'
elif num == RUBY_Qtrue:
- print('true', file=result)
+ print >> result, 'true'
elif num == RUBY_Qnil:
- print('nil', file=result)
+ print >> result, 'nil'
elif num == RUBY_Qundef:
- print('undef', file=result)
+ print >> result, 'undef'
elif fixnum_p(num):
- print(num >> 1, file=result)
+ print >> result, num >> 1
elif flonum_p(num):
- append_expression(debugger, "rb_float_value(%0#x)" % val.GetValueAsUnsigned(), result)
- elif static_sym_p(num):
- if num < 128:
- print("T_SYMBOL: %c" % num, file=result)
- else:
- print("T_SYMBOL: (%x)" % num, file=result)
- append_expression(debugger, "rb_id2name(%0#x)" % (num >> 8), result)
+ debugger.HandleCommand("print rb_float_value(%0#x)" % val.GetValueAsUnsigned())
elif num & RUBY_IMMEDIATE_MASK:
- print('immediate(%x)' % num, file=result)
+ print >> result, 'immediate(%x)' % num
else:
tRBasic = target.FindFirstType("struct RBasic").GetPointerType()
- tRValue = target.FindFirstType("struct RVALUE")
-
val = val.Cast(tRBasic)
flags = val.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
- flaginfo = ""
-
- page = get_page(lldb, target, val)
- page_type = target.FindFirstType("struct heap_page").GetPointerType()
- page.Cast(page_type)
-
- dump_bits(target, result, page, val.GetValueAsUnsigned())
-
if (flags & RUBY_FL_PROMOTED) == RUBY_FL_PROMOTED:
- flaginfo += "[PROMOTED] "
- if (flags & RUBY_FL_FREEZE) == RUBY_FL_FREEZE:
- flaginfo += "[FROZEN] "
+ print >> result, "[PROMOTED] "
flType = flags & RUBY_T_MASK
if flType == RUBY_T_NONE:
- print('T_NONE: %s%s' % (flaginfo, val.Dereference()), file=result)
+ print >> result, 'T_NONE: %s' % val.Dereference()
elif flType == RUBY_T_NIL:
- print('T_NIL: %s%s' % (flaginfo, val.Dereference()), file=result)
+ print >> result, 'T_NIL: %s' % val.Dereference()
elif flType == RUBY_T_OBJECT:
- result.write('T_OBJECT: %s' % flaginfo)
- append_expression(debugger, "*(struct RObject*)%0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_CLASS or flType == RUBY_T_MODULE or flType == RUBY_T_ICLASS:
- result.write('T_%s: %s' % ('CLASS' if flType == RUBY_T_CLASS else 'MODULE' if flType == RUBY_T_MODULE else 'ICLASS', flaginfo))
- append_expression(debugger, "*(struct RClass*)%0#x" % val.GetValueAsUnsigned(), result)
- tRClass = target.FindFirstType("struct RClass")
- if not val.Cast(tRClass).GetChildMemberWithName("ptr").IsValid():
- append_expression(debugger, "*(struct rb_classext_struct*)%0#x" % (val.GetValueAsUnsigned() + tRClass.GetByteSize()), result)
+ tRObject = target.FindFirstType("struct RObject").GetPointerType()
+ val = val.Cast(tRObject)
+ print >> result, 'T_OBJECT: %s' % val.Dereference()
elif flType == RUBY_T_STRING:
- result.write('T_STRING: %s' % flaginfo)
- encidx = ((flags & RUBY_ENCODING_MASK)>>RUBY_ENCODING_SHIFT)
- encname = target.FindFirstType("enum ruby_preserved_encindex").GetEnumMembers().GetTypeEnumMemberAtIndex(encidx).GetName()
- if encname is not None:
- result.write('[%s] ' % encname[14:])
- else:
- result.write('[enc=%d] ' % encidx)
tRString = target.FindFirstType("struct RString").GetPointerType()
- ptr, len = string2cstr(val.Cast(tRString))
- if len == 0:
- result.write("(empty)\n")
+ val = val.Cast(tRString)
+ if flags & RSTRING_NOEMBED:
+ print >> result, val.GetValueForExpressionPath("->as.heap")
else:
- append_expression(debugger, "*(const char (*)[%d])%0#x" % (len, ptr), result)
- elif flType == RUBY_T_SYMBOL:
- result.write('T_SYMBOL: %s' % flaginfo)
- tRSymbol = target.FindFirstType("struct RSymbol").GetPointerType()
- val = val.Cast(tRSymbol)
- append_expression(debugger, '(ID)%0#x ' % val.GetValueForExpressionPath("->id").GetValueAsUnsigned(), result)
- tRString = target.FindFirstType("struct RString").GetPointerType()
- output_string(debugger, result, val.GetValueForExpressionPath("->fstr").Cast(tRString))
+ print >> result, val.GetValueForExpressionPath("->as.ary")
elif flType == RUBY_T_ARRAY:
tRArray = target.FindFirstType("struct RArray").GetPointerType()
val = val.Cast(tRArray)
if flags & RUBY_FL_USER1:
- len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5|RUBY_FL_USER6|RUBY_FL_USER7|RUBY_FL_USER8|RUBY_FL_USER9)) >> (RUBY_FL_USHIFT+3))
- ptr = val.GetValueForExpressionPath("->as.ary")
- else:
- len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
- ptr = val.GetValueForExpressionPath("->as.heap.ptr")
- result.write("T_ARRAY: %slen=%d" % (flaginfo, len))
- if flags & RUBY_FL_USER1:
- result.write(" (embed)")
- elif flags & RUBY_FL_USER2:
- shared = val.GetValueForExpressionPath("->as.heap.aux.shared").GetValueAsUnsigned()
- result.write(" (shared) shared=%016x" % shared)
- else:
- capa = val.GetValueForExpressionPath("->as.heap.aux.capa").GetValueAsSigned()
- result.write(" (ownership) capa=%d" % capa)
- if len == 0:
- result.write(" {(empty)}\n")
- else:
- result.write("\n")
- if ptr.GetValueAsSigned() == 0:
- append_expression(debugger, "-fx -- ((struct RArray*)%0#x)->as.ary" % val.GetValueAsUnsigned(), result)
+ len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4)) >> (RUBY_FL_USHIFT+3))
+ print >> result, "T_ARRAY: len=%d (embed)" % len
+ if len == 0:
+ print >> result, "{(empty)}"
else:
- append_expression(debugger, "-Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()), result)
- elif flType == RUBY_T_HASH:
- result.write("T_HASH: %s" % flaginfo)
- append_expression(debugger, "*(struct RHash *) %0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_BIGNUM:
- tRBignum = target.FindFirstType("struct RBignum").GetPointerType()
- val = val.Cast(tRBignum)
- sign = '+' if (flags & RUBY_FL_USER1) != 0 else '-'
- if flags & RUBY_FL_USER2:
- len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5)) >> (RUBY_FL_USHIFT+3))
- print("T_BIGNUM: sign=%s len=%d (embed)" % (sign, len), file=result)
- append_expression(debugger, "((struct RBignum *) %0#x)->as.ary" % val.GetValueAsUnsigned(), result)
+ print >> result, val.GetValueForExpressionPath("->as.ary")
else:
len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
- print("T_BIGNUM: sign=%s len=%d" % (sign, len), file=result)
- print(val.Dereference(), file=result)
- append_expression(debugger, "-Z %x -fx -- (const BDIGIT*)((struct RBignum*)%d)->as.heap.digits" % (len, val.GetValueAsUnsigned()), result)
- # append_expression(debugger, "((struct RBignum *) %0#x)->as.heap.digits / %d" % (val.GetValueAsUnsigned(), len), result)
- elif flType == RUBY_T_FLOAT:
- append_expression(debugger, "((struct RFloat *)%d)->float_value" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_RATIONAL:
- tRRational = target.FindFirstType("struct RRational").GetPointerType()
- val = val.Cast(tRRational)
- lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->num"))
- output = result.GetOutput()
- result.Clear()
- result.write("(Rational) " + output.rstrip() + " / ")
- lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->den"))
- elif flType == RUBY_T_COMPLEX:
- tRComplex = target.FindFirstType("struct RComplex").GetPointerType()
- val = val.Cast(tRComplex)
- lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->real"))
- real = result.GetOutput().rstrip()
- result.Clear()
- lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->imag"))
- imag = result.GetOutput().rstrip()
- result.Clear()
- if not imag.startswith("-"):
- imag = "+" + imag
- print("(Complex) " + real + imag + "i", file=result)
- elif flType == RUBY_T_REGEXP:
- tRRegex = target.FindFirstType("struct RRegexp").GetPointerType()
- val = val.Cast(tRRegex)
- print("(Regex) ->src {", file=result)
- lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->src"))
- print("}", file=result)
- elif flType == RUBY_T_DATA:
- tRTypedData = target.FindFirstType("struct RTypedData").GetPointerType()
- val = val.Cast(tRTypedData)
- flag = val.GetValueForExpressionPath("->typed_flag")
- if flag.GetValueAsUnsigned() == 1:
- print("T_DATA: %s" % val.GetValueForExpressionPath("->type->wrap_struct_name"), file=result)
- append_expression(debugger, "*(struct RTypedData *) %0#x" % val.GetValueAsUnsigned(), result)
- else:
- print("T_DATA:", file=result)
- append_expression(debugger, "*(struct RData *) %0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_NODE:
- tRTypedData = target.FindFirstType("struct RNode").GetPointerType()
- nd_type = (flags & RUBY_NODE_TYPEMASK) >> RUBY_NODE_TYPESHIFT
- append_expression(debugger, "(node_type) %d" % nd_type, result)
- val = val.Cast(tRTypedData)
- append_expression(debugger, "*(struct RNode *) %0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_MOVED:
- tRTypedData = target.FindFirstType("struct RMoved").GetPointerType()
- val = val.Cast(tRTypedData)
- append_expression(debugger, "*(struct RMoved *) %0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_MATCH:
- tRTypedData = target.FindFirstType("struct RMatch").GetPointerType()
- val = val.Cast(tRTypedData)
- append_expression(debugger, "*(struct RMatch *) %0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_IMEMO:
- # I'm not sure how to get IMEMO_MASK out of lldb. It's not in globals()
- imemo_type = (flags >> RUBY_FL_USHIFT) & 0x0F # IMEMO_MASK
-
- print("T_IMEMO: ", file=result)
- append_expression(debugger, "(enum imemo_type) %d" % imemo_type, result)
- append_expression(debugger, "*(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_STRUCT:
- tRTypedData = target.FindFirstType("struct RStruct").GetPointerType()
- val = val.Cast(tRTypedData)
- append_expression(debugger, "*(struct RStruct *) %0#x" % val.GetValueAsUnsigned(), result)
- elif flType == RUBY_T_ZOMBIE:
- tRZombie = target.FindFirstType("struct RZombie").GetPointerType()
- val = val.Cast(tRZombie)
- append_expression(debugger, "*(struct RZombie *) %0#x" % val.GetValueAsUnsigned(), result)
- else:
- print("Not-handled type %0#x" % flType, file=result)
- print(val, file=result)
+ print >> result, "T_ARRAY: len=%d " % len
+ #print >> result, val.GetValueForExpressionPath("->as.heap")
+ if flags & RUBY_FL_USER2:
+ shared = val.GetValueForExpressionPath("->as.heap.aux.shared").GetValueAsUnsigned()
+ print >> result, "(shared) shared=%016x " % shared
+ else:
+ capa = val.GetValueForExpressionPath("->as.heap.aux.capa").GetValueAsSigned()
+ print >> result, "(ownership) capa=%d " % capa
+ if len == 0:
+ print >> result, "{(empty)}"
+ else:
+ debugger.HandleCommand("expression -Z %d -fx -- (const VALUE*)((struct RArray*)%d)->as.heap.ptr" % (len, val.GetValueAsUnsigned()))
+ debugger.HandleCommand("p (struct RArray *) %0#x" % val.GetValueAsUnsigned())
def count_objects(debugger, command, ctx, result, internal_dict):
objspace = ctx.frame.EvaluateExpression("ruby_current_vm->objspace")
@@ -445,7 +141,7 @@ def count_objects(debugger, command, ctx, result, internal_dict):
counts[t] = 0
for i in range(0, num_pages):
- print("\rcounting... %d/%d" % (i, num_pages), end="")
+ print "\rcounting... %d/%d" % (i, num_pages),
page = objspace.GetValueForExpressionPath('.heap_pages.sorted[%d]' % i)
p = page.GetChildMemberWithName('start')
num_slots = page.GetChildMemberWithName('total_slots').unsigned
@@ -456,43 +152,13 @@ def count_objects(debugger, command, ctx, result, internal_dict):
counts[obj_type] += 1
total += num_slots
- print("\rTOTAL: %d, FREE: %d" % (total, counts[0x00]))
+ print "\rTOTAL: %d, FREE: %d" % (total, counts[0x00])
for sym in value_types:
- print("%s: %d" % (sym, counts[globals()[sym]]))
+ print "%s: %d" % (sym, counts[globals()[sym]])
def stack_dump_raw(debugger, command, ctx, result, internal_dict):
ctx.frame.EvaluateExpression("rb_vmdebug_stack_dump_raw_current()")
-def check_bits(page, bitmap_name, bitmap_index, bitmap_bit, v):
- bits = page.GetChildMemberWithName(bitmap_name)
- plane = bits.GetChildAtIndex(bitmap_index).GetValueAsUnsigned()
- if (plane & bitmap_bit) != 0:
- return v
- else:
- return ' '
-
-def heap_page_body(debugger, command, ctx, result, internal_dict):
- target = debugger.GetSelectedTarget()
- process = target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
-
- val = frame.EvaluateExpression(command)
- page = get_page_body(lldb, target, val)
- print("Page body address: ", page.GetAddress(), file=result)
- print(page, file=result)
-
-def get_page_body(lldb, target, val):
- tHeapPageBody = target.FindFirstType("struct heap_page_body")
- addr = val.GetValueAsUnsigned()
- page_addr = addr & ~(HEAP_PAGE_ALIGN_MASK)
- address = lldb.SBAddress(page_addr, target)
- return target.CreateValueFromAddress("page", address, tHeapPageBody)
-
-def get_page(lldb, target, val):
- body = get_page_body(lldb, target, val)
- return body.GetValueForExpressionPath("->header.page")
-
def dump_node(debugger, command, ctx, result, internal_dict):
args = shlex.split(command)
if not args:
@@ -500,250 +166,12 @@ def dump_node(debugger, command, ctx, result, internal_dict):
node = args[0]
dump = ctx.frame.EvaluateExpression("(struct RString*)rb_parser_dump_tree((NODE*)(%s), 0)" % node)
- output_string(ctx, result, dump)
-
-def rb_backtrace(debugger, command, result, internal_dict):
- if not ('RUBY_Qfalse' in globals()):
- lldb_init(debugger)
- bt = BackTrace(debugger, command, result, internal_dict)
- frame = bt.frame
-
- if command:
- if frame.IsValid():
- val = frame.EvaluateExpression(command)
- else:
- val = target.EvaluateExpression(command)
-
- error = val.GetError()
- if error.Fail():
- print >> result, error
- return
- else:
- print("Need an EC for now")
-
- bt.print_bt(val)
-
-def dump_bits(target, result, page, object_address, end = "\n"):
- tRValue = target.FindFirstType("struct RVALUE")
- tUintPtr = target.FindFirstType("uintptr_t") # bits_t
-
- num_in_page = (object_address & HEAP_PAGE_ALIGN_MASK) // tRValue.GetByteSize();
- bits_bitlength = tUintPtr.GetByteSize() * 8
- bitmap_index = num_in_page // bits_bitlength
- bitmap_offset = num_in_page & (bits_bitlength - 1)
- bitmap_bit = 1 << bitmap_offset
-
- print("bits: [%s%s%s%s%s]" % (
- check_bits(page, "uncollectible_bits", bitmap_index, bitmap_bit, "L"),
- check_bits(page, "mark_bits", bitmap_index, bitmap_bit, "M"),
- check_bits(page, "pinned_bits", bitmap_index, bitmap_bit, "P"),
- check_bits(page, "marking_bits", bitmap_index, bitmap_bit, "R"),
- check_bits(page, "wb_unprotected_bits", bitmap_index, bitmap_bit, "U"),
- ), end=end, file=result)
-
-class HeapPageIter:
- def __init__(self, page, target):
- self.page = page
- self.target = target
- self.start = page.GetChildMemberWithName('start').GetValueAsUnsigned();
- self.num_slots = page.GetChildMemberWithName('total_slots').unsigned
- self.slot_size = page.GetChildMemberWithName('heap').GetChildMemberWithName('slot_size').unsigned
- self.counter = 0
- self.tRBasic = target.FindFirstType("struct RBasic")
- self.tRValue = target.FindFirstType("struct RVALUE")
-
- def is_valid(self):
- heap_page_header_size = self.target.FindFirstType("struct heap_page_header").GetByteSize()
- rvalue_size = self.slot_size
- heap_page_obj_limit = int((HEAP_PAGE_SIZE - heap_page_header_size) / self.slot_size)
-
- return (heap_page_obj_limit - 1) <= self.num_slots <= heap_page_obj_limit
-
- def __iter__(self):
- return self
-
- def __next__(self):
- if self.counter < self.num_slots:
- obj_addr_i = self.start + (self.counter * self.slot_size)
- obj_addr = lldb.SBAddress(obj_addr_i, self.target)
- slot_info = (self.counter, obj_addr_i, self.target.CreateValueFromAddress("object", obj_addr, self.tRBasic))
- self.counter += 1
-
- return slot_info
- else:
- raise StopIteration
-
-
-def dump_page_internal(page, target, process, thread, frame, result, debugger, highlight=None):
- if not ('RUBY_Qfalse' in globals()):
- lldb_init(debugger)
-
- ruby_type_map = ruby_types(debugger)
-
- freelist = []
- fl_start = page.GetChildMemberWithName('freelist').GetValueAsUnsigned()
- tRVALUE = target.FindFirstType("struct RVALUE")
-
- while fl_start > 0:
- freelist.append(fl_start)
- obj_addr = lldb.SBAddress(fl_start, target)
- obj = target.CreateValueFromAddress("object", obj_addr, tRVALUE)
- fl_start = obj.GetChildMemberWithName("as").GetChildMemberWithName("free").GetChildMemberWithName("next").GetValueAsUnsigned()
-
-
- page_iter = HeapPageIter(page, target)
- if page_iter.is_valid():
- for (page_index, obj_addr, obj) in page_iter:
- dump_bits(target, result, page, obj_addr, end= " ")
- flags = obj.GetChildMemberWithName('flags').GetValueAsUnsigned()
- flType = flags & RUBY_T_MASK
-
- flidx = ' '
- if flType == RUBY_T_NONE:
- try:
- flidx = "%3d" % freelist.index(obj_addr)
- except ValueError:
- flidx = ' -1'
-
- if flType == RUBY_T_NONE:
- klass = obj.GetChildMemberWithName('klass').GetValueAsUnsigned()
- result_str = "%s idx: [%3d] freelist_idx: {%s} Addr: %0#x (flags: %0#x, next: %0#x)" % (rb_type(flags, ruby_type_map), page_index, flidx, obj_addr, flags, klass)
- else:
- result_str = "%s idx: [%3d] freelist_idx: {%s} Addr: %0#x (flags: %0#x)" % (rb_type(flags, ruby_type_map), page_index, flidx, obj_addr, flags)
-
- if highlight == obj_addr:
- result_str = ' '.join([result_str, "<<<<<"])
-
- print(result_str, file=result)
- else:
- print("%s is not a valid heap page" % page, file=result)
-
-
-
-def dump_page(debugger, command, result, internal_dict):
- target = debugger.GetSelectedTarget()
- process = target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
-
- tHeapPageP = target.FindFirstType("struct heap_page").GetPointerType()
- page = frame.EvaluateExpression(command)
- page = page.Cast(tHeapPageP)
-
- dump_page_internal(page, target, process, thread, frame, result, debugger)
-
-
-def dump_page_rvalue(debugger, command, result, internal_dict):
- target = debugger.GetSelectedTarget()
- process = target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
-
- val = frame.EvaluateExpression(command)
- page = get_page(lldb, target, val)
- page_type = target.FindFirstType("struct heap_page").GetPointerType()
- page.Cast(page_type)
-
- dump_page_internal(page, target, process, thread, frame, result, debugger, highlight=val.GetValueAsUnsigned())
-
-
-
-def rb_type(flags, ruby_types):
- flType = flags & RUBY_T_MASK
- return "%-10s" % (ruby_types.get(flType, ("%0#x" % flType)))
-
-def ruby_types(debugger):
- target = debugger.GetSelectedTarget()
-
- types = {}
- for enum in target.FindFirstGlobalVariable('ruby_dummy_gdb_enums'):
- enum = enum.GetType()
- members = enum.GetEnumMembers()
- for i in range(0, members.GetSize()):
- member = members.GetTypeEnumMemberAtIndex(i)
- name = member.GetName()
- value = member.GetValueAsUnsigned()
-
- if name.startswith('RUBY_T_'):
- types[value] = name.replace('RUBY_', '')
-
- return types
-
-def rb_ary_entry(target, ary, idx, result):
- tRArray = target.FindFirstType("struct RArray").GetPointerType()
- ary = ary.Cast(tRArray)
- flags = ary.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
-
- if flags & RUBY_FL_USER1:
- ptr = ary.GetValueForExpressionPath("->as.ary")
- else:
- ptr = ary.GetValueForExpressionPath("->as.heap.ptr")
-
- ptr_addr = ptr.GetValueAsUnsigned() + (idx * ptr.GetType().GetByteSize())
- return target.CreateValueFromAddress("ary_entry[%d]" % idx, lldb.SBAddress(ptr_addr, target), ptr.GetType().GetPointeeType())
-
-def rb_id_to_serial(id_val):
- if id_val > tLAST_OP_ID:
- return id_val >> RUBY_ID_SCOPE_SHIFT
- else:
- return id_val
-
-def rb_id2str(debugger, command, result, internal_dict):
- if not ('RUBY_Qfalse' in globals()):
- lldb_init(debugger)
-
- target = debugger.GetSelectedTarget()
- process = target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
- global_symbols = target.FindFirstGlobalVariable("ruby_global_symbols")
-
- id_val = frame.EvaluateExpression(command).GetValueAsUnsigned()
- num = rb_id_to_serial(id_val)
-
- last_id = global_symbols.GetChildMemberWithName("last_id").GetValueAsUnsigned()
- ID_ENTRY_SIZE = 2
- ID_ENTRY_UNIT = int(target.FindFirstGlobalVariable("ID_ENTRY_UNIT").GetValue())
-
- ids = global_symbols.GetChildMemberWithName("ids")
-
- if (num <= last_id):
- idx = num // ID_ENTRY_UNIT
- ary = rb_ary_entry(target, ids, idx, result)
- pos = (num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE
- id_str = rb_ary_entry(target, ary, pos, result)
- lldb_inspect(debugger, target, result, id_str)
-# END FUNCTION STYLE DECLS
-
-
-load_dir, _ = os.path.split(os.path.realpath(__file__))
-
-for fname in glob.glob(f"{load_dir}/lldb_rb/commands/*_command.py"):
- _, basename = os.path.split(fname)
- mname, _ = os.path.splitext(basename)
-
- exec(f"import lldb_rb.commands.{mname}")
+ output_string(ctx, dump)
def __lldb_init_module(debugger, internal_dict):
- # Register all classes that subclass RbBaseCommand
-
- for memname, mem in inspect.getmembers(sys.modules["lldb_rb.rb_base_command"]):
- if memname == "RbBaseCommand":
- for sclass in mem.__subclasses__():
- sclass.register_lldb_command(debugger, f"{__name__}.{sclass.__module__}")
-
-
- ## FUNCTION INITS - These should be removed when converted to class commands
- debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp old_rp")
+ debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp rp")
debugger.HandleCommand("command script add -f lldb_cruby.count_objects rb_count_objects")
debugger.HandleCommand("command script add -f lldb_cruby.stack_dump_raw SDR")
debugger.HandleCommand("command script add -f lldb_cruby.dump_node dump_node")
- debugger.HandleCommand("command script add -f lldb_cruby.heap_page_body heap_page_body")
- debugger.HandleCommand("command script add -f lldb_cruby.rb_backtrace rbbt")
- debugger.HandleCommand("command script add -f lldb_cruby.dump_page dump_page")
- debugger.HandleCommand("command script add -f lldb_cruby.dump_page_rvalue dump_page_rvalue")
- debugger.HandleCommand("command script add -f lldb_cruby.rb_id2str old_rb_id2str")
-
- lldb_rb.rb_base_command.RbBaseCommand.lldb_init(debugger)
-
- print("lldb scripts for ruby has been installed.")
+ lldb_init(debugger)
+ print "lldb scripts for ruby has been installed."
diff --git a/misc/lldb_disasm.py b/misc/lldb_disasm.py
deleted file mode 100644
index ab759f009a..0000000000
--- a/misc/lldb_disasm.py
+++ /dev/null
@@ -1,250 +0,0 @@
-#!/usr/bin/env python
-#coding: utf-8
-#
-# Usage: run `command script import -r misc/lldb_disasm.py` on LLDB
-#
-#
-# (lldb) p iseq
-# (rb_iseq_t *) $147 = 0x0000000101068400
-# (lldb) rbdisasm iseq
-# 0000 putspecialobject( 3 )
-# 0002 putnil
-# 0003 defineclass( ID: 0x560b, (rb_iseq_t *)0x1010681d0, 2 )
-# 0007 pop
-# 0008 putspecialobject( 3 )
-# 0010 putnil
-# 0011 defineclass( ID: 0x56eb, (rb_iseq_t *)0x101063b58, 2 )
-# 0015 leave
-
-
-import lldb
-import os
-import shlex
-
-class IseqDisassembler:
- TS_VARIABLE = b'.'[0]
- TS_CALLDATA = b'C'[0]
- TS_CDHASH = b'H'[0]
- TS_IC = b'K'[0]
- TS_IVC = b'A'[0]
- TS_ICVARC = b'J'[0]
- TS_ID = b'I'[0]
- TS_ISE = b'T'[0]
- TS_ISEQ = b'S'[0]
- TS_OFFSET = b'O'[0]
- TS_VALUE = b'V'[0]
- TS_LINDEX = b'L'[0]
- TS_FUNCPTR = b'F'[0]
- TS_NUM = b'N'[0]
- TS_BUILTIN = b'R'[0]
-
- ISEQ_OPT_DISPATCH = {
- TS_BUILTIN: "(rb_builtin_function *)%0#x",
- TS_NUM: "%d",
- TS_FUNCPTR: "(rb_insn_func_t) %0#x",
- TS_LINDEX: "%d",
- TS_VALUE: "(VALUE)%0#x",
- TS_OFFSET: "%d",
- TS_ISEQ: "(rb_iseq_t *)%0#x",
- TS_ISE: "(iseq_inline_storage_entry *)%0#x",
- TS_ID: "ID: %0#x",
- TS_IVC: "(struct iseq_inline_iv_cache_entry *)%0#x",
- TS_ICVARC: "(struct iseq_inline_cvar_cache_entry *)%0#x",
- TS_IC: "(struct iseq_inline_cache_entry *)%0#x",
- TS_CDHASH: "CDHASH (VALUE)%0#x",
- TS_CALLDATA: "(struct rb_call_data *)%0#x",
- TS_VARIABLE: "VARIABLE %0#x",
- }
-
- def __init__(self, debugger, command, result, internal_dict):
- self.debugger = debugger
- self.command = command
- self.result = result
- self.internal_dict = internal_dict
-
- self.target = debugger.GetSelectedTarget()
- self.insns_address_table = self.__get_insns_address_table()
- self.process = self.target.GetProcess()
- self.thread = self.process.GetSelectedThread()
- self.frame = self.thread.GetSelectedFrame()
- self.addr2insn = self.build_addr2insn(self.target)
- self.tChar = self.target.FindFirstType("char")
-
- def disasm(self, val):
- tRbISeq = self.target.FindFirstType("struct rb_iseq_struct").GetPointerType()
- val = val.Cast(tRbISeq)
- iseq_size = val.GetValueForExpressionPath("->body->iseq_size").GetValueAsUnsigned()
- iseqs = val.GetValueForExpressionPath("->body->iseq_encoded")
- idx = 0
- print("PC IDX insn_name(operands) ", file=self.result)
- while idx < iseq_size:
- m = self.iseq_extract_values(self.debugger, self.target, self.process, self.result, iseqs, idx)
- if m < 1:
- print("Error decoding", file=self.result)
- return
- else:
- idx += m
-
- def build_addr2insn(self, target):
- tIntPtr = target.FindFirstType("intptr_t")
- size = target.EvaluateExpression('ruby_vminsn_type::VM_INSTRUCTION_SIZE').unsigned
- sizeOfIntPtr = tIntPtr.GetByteSize()
- addr_of_table = self.insns_address_table.GetStartAddress().GetLoadAddress(target)
-
- my_dict = {}
-
- for insn in range(size):
- addr_in_table = addr_of_table + (insn * sizeOfIntPtr)
- addr = lldb.SBAddress(addr_in_table, target)
- machine_insn = target.CreateValueFromAddress("insn", addr, tIntPtr).GetValueAsUnsigned()
- my_dict[machine_insn] = insn
-
- return my_dict
-
- def rb_vm_insn_addr2insn2(self, target, result, wanted_addr):
- return self.addr2insn.get(wanted_addr)
-
- def iseq_extract_values(self, debugger, target, process, result, iseqs, n):
- tValueP = target.FindFirstType("VALUE")
- sizeofValueP = tValueP.GetByteSize()
- pc = iseqs.unsigned + (n * sizeofValueP)
- insn = target.CreateValueFromAddress("i", lldb.SBAddress(pc, target), tValueP)
- addr = insn.GetValueAsUnsigned()
- orig_insn = self.rb_vm_insn_addr2insn2(target, result, addr)
-
- name = self.insn_name(target, process, result, orig_insn)
- length = self.insn_len(target, orig_insn)
- op_str = self.insn_op_types(target, process, result, orig_insn)
- op_types = bytes(op_str, 'utf-8')
-
- if length != (len(op_types) + 1):
- print("error decoding iseqs", file=result)
- return -1
-
- print("%0#14x %04d %s" % (pc, n, name), file=result, end="")
-
- if length == 1:
- print("", file=result)
- return length
-
- print("(", end="", file=result)
- for idx, op_type in enumerate(op_types):
- if idx == 0:
- print(" ", end="", file=result)
- else:
- print(", ", end="", file=result)
-
- opAddr = lldb.SBAddress(iseqs.unsigned + ((n + idx + 1) * sizeofValueP), target)
- opValue = target.CreateValueFromAddress("op", opAddr, tValueP)
- op = opValue.GetValueAsUnsigned()
- print(self.ISEQ_OPT_DISPATCH.get(op_type) % op, end="", file=result)
-
- print(" )", file=result)
- return length
-
- def insn_len(self, target, offset):
- size_of_char = self.tChar.GetByteSize()
-
- symbol = target.FindSymbols("rb_vm_insn_len_info")[0].GetSymbol()
- section = symbol.GetStartAddress().GetSection()
- addr_of_table = symbol.GetStartAddress().GetOffset()
-
- error = lldb.SBError()
- length = section.GetSectionData().GetUnsignedInt8(error, addr_of_table + (offset * size_of_char))
-
- if error.Success():
- return length
- else:
- print("error getting length: ", error)
-
- def insn_op_types(self, target, process, result, insn):
- tUShort = target.FindFirstType("unsigned short")
-
- size_of_short = tUShort.GetByteSize()
- size_of_char = self.tChar.GetByteSize()
-
- symbol = target.FindSymbols("rb_vm_insn_op_offset")[0].GetSymbol()
- section = symbol.GetStartAddress().GetSection()
- addr_of_table = symbol.GetStartAddress().GetOffset()
-
- addr_in_table = addr_of_table + (insn * size_of_short)
-
- error = lldb.SBError()
- offset = section.GetSectionData().GetUnsignedInt16(error, addr_in_table)
-
- if not error.Success():
- print("error getting op type offset: ", error)
-
- symbol = target.FindSymbols("rb_vm_insn_op_base")[0].GetSymbol()
- section = symbol.GetStartAddress().GetSection()
- addr_of_table = symbol.GetStartAddress().GetOffset()
- addr_in_name_table = addr_of_table + (offset * size_of_char)
-
- error = lldb.SBError()
- types = section.GetSectionData().GetString(error, addr_in_name_table)
- if error.Success():
- return types
- else:
- print("error getting op types: ", error)
-
- def insn_name_table_offset(self, target, offset):
- tUShort = target.FindFirstType("unsigned short")
- size_of_short = tUShort.GetByteSize()
-
- symbol = target.FindSymbols("rb_vm_insn_name_offset")[0].GetSymbol()
- section = symbol.GetStartAddress().GetSection()
- table_offset = symbol.GetStartAddress().GetOffset()
-
- table_offset = table_offset + (offset * size_of_short)
-
- error = lldb.SBError()
- offset = section.GetSectionData().GetUnsignedInt16(error, table_offset)
-
- if error.Success():
- return offset
- else:
- print("error getting insn name table offset: ", error)
-
- def insn_name(self, target, process, result, offset):
- symbol = target.FindSymbols("rb_vm_insn_name_base")[0].GetSymbol()
- section = symbol.GetStartAddress().GetSection()
- addr_of_table = symbol.GetStartAddress().GetOffset()
-
- name_table_offset = self.insn_name_table_offset(target, offset)
- addr_in_name_table = addr_of_table + name_table_offset
-
- error = lldb.SBError()
- name = section.GetSectionData().GetString(error, addr_in_name_table)
-
- if error.Success():
- return name
- else:
- print('error getting insn name', error)
-
- def __get_insns_address_table(self):
- module = self.target.FindSymbols("vm_exec_core")[0].GetModule()
-
- for symbol in module:
- if "insns_address_table" in symbol.name and symbol.GetType() == lldb.eSymbolTypeData:
- print(f"found symbol {symbol.name}")
- return symbol
-
-
-def disasm(debugger, command, result, internal_dict):
- disassembler = IseqDisassembler(debugger, command, result, internal_dict)
- frame = disassembler.frame
-
- if frame.IsValid():
- val = frame.EvaluateExpression(command)
- else:
- val = target.EvaluateExpression(command)
- error = val.GetError()
- if error.Fail():
- print >> result, error
- return
-
- disassembler.disasm(val);
-
-def __lldb_init_module(debugger, internal_dict):
- debugger.HandleCommand("command script add -f lldb_disasm.disasm rbdisasm")
- print("lldb Ruby disasm installed.")
diff --git a/misc/lldb_rb/commands/command_template.py b/misc/lldb_rb/commands/command_template.py
deleted file mode 100644
index 21014a993e..0000000000
--- a/misc/lldb_rb/commands/command_template.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# This is a command template for implementing a helper function inside LLDB. To
-# use this file
-# 1. Copy it and rename the copy so it ends with `_command.py`.
-# 2. Rename the class to something descriptive that ends with Command.
-# 3. Change the program variable to be a descriptive command name
-# 4. Ensure you are inheriting from RbBaseCommand or another command that
-# implements the same interface
-
-import lldb
-
-from lldb_rb.constants import *
-from lldb_rb.rb_base_command import RbBaseCommand
-
-# This test command inherits from RbBaseCommand which provides access to Ruby
-# globals and utility helpers
-class TestCommand(RbBaseCommand):
- # program is the keyword the user will type in lldb to execute this command
- program = "test"
-
- # help_string will be displayed in lldb when the user uses the help functions
- help_string = "This is a test command to show how to implement lldb commands"
-
- # call is where our command logic will be implemented
- def call(self, debugger, command, exe_ctx, result):
- # This method will be called once the LLDB environment has been setup.
- # You will have access to self.target, self.process, self.frame, and
- # self.thread
- #
- # This is where we should implement our command logic
- pass
diff --git a/misc/lldb_rb/commands/heap_page_command.py b/misc/lldb_rb/commands/heap_page_command.py
deleted file mode 100644
index b56a3eae4e..0000000000
--- a/misc/lldb_rb/commands/heap_page_command.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import lldb
-
-from lldb_rb.constants import *
-from lldb_rb.rb_base_command import RbBaseCommand
-
-class HeapPageCommand(RbBaseCommand):
- program = "heap_page"
- help_string = "prints out 'struct heap_page' for a VALUE pointer in the page"
-
- def call(self, debugger, command, exe_ctx, result):
- self.t_heap_page_body = self.target.FindFirstType("struct heap_page_body")
- self.t_heap_page_ptr = self.target.FindFirstType("struct heap_page").GetPointerType()
-
- page = self._get_page(self.frame.EvaluateExpression(command))
- page.Cast(self.t_heap_page_ptr)
-
- self._append_expression(debugger, "(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
- self._append_expression(debugger, "*(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
-
- def _get_page(self, val):
- addr = val.GetValueAsUnsigned()
- page_addr = addr & ~(HEAP_PAGE_ALIGN_MASK)
- address = lldb.SBAddress(page_addr, self.target)
- body = self.target.CreateValueFromAddress("page", address, self.t_heap_page_body)
-
- return body.GetValueForExpressionPath("->header.page")
diff --git a/misc/lldb_rb/commands/print_flags_command.py b/misc/lldb_rb/commands/print_flags_command.py
deleted file mode 100644
index 00da4834bf..0000000000
--- a/misc/lldb_rb/commands/print_flags_command.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import lldb
-import re
-
-from lldb_rb.constants import *
-from lldb_rb.rb_base_command import RbBaseCommand
-
-class PrintFlagsCommand(RbBaseCommand):
- program = "print_flags"
-
- help_string = "Print out the individial flags of an RVALUE object in human readable format"
-
- # call is where our command logic will be implemented
- def call(self, debugger, command, exe_ctx, result):
- rclass_t = self.target.FindFirstType("struct RBasic")
- rcass_ptr = self.target.EvaluateExpression(command).Cast(rclass_t.GetPointerType())
- obj_flags = rcass_ptr.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
-
- flags = [
- "RUBY_FL_WB_PROTECTED", "RUBY_FL_PROMOTED0", "RUBY_FL_PROMOTED1", "RUBY_FL_FINALIZE",
- "RUBY_FL_SHAREABLE", "RUBY_FL_EXIVAR", "RUBY_FL_FREEZE",
- "RUBY_FL_USER0", "RUBY_FL_USER1", "RUBY_FL_USER2", "RUBY_FL_USER3", "RUBY_FL_USER4",
- "RUBY_FL_USER5", "RUBY_FL_USER6", "RUBY_FL_USER7", "RUBY_FL_USER8", "RUBY_FL_USER9",
- "RUBY_FL_USER10", "RUBY_FL_USER11", "RUBY_FL_USER12", "RUBY_FL_USER13", "RUBY_FL_USER14",
- "RUBY_FL_USER15", "RUBY_FL_USER16", "RUBY_FL_USER17", "RUBY_FL_USER18"
- ]
-
- types_index = {v: k for k, v in self.ruby_globals.items() if re.match(r'RUBY_T_', k)}
- print("TYPE: {}".format(types_index[obj_flags & self.ruby_globals["RUBY_T_MASK"]]))
- for flag in flags:
- output = "{} : {}".format(flag, "1" if (obj_flags & self.ruby_globals[flag]) else "0")
- print(output, file=result)
diff --git a/misc/lldb_rb/commands/rb_id2str_command.py b/misc/lldb_rb/commands/rb_id2str_command.py
deleted file mode 100644
index 6ee859ebf6..0000000000
--- a/misc/lldb_rb/commands/rb_id2str_command.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import lldb
-
-from lldb_rb.constants import *
-from lldb_rb.utils import *
-from lldb_rb.rb_base_command import RbBaseCommand
-
-class RbID2StrCommand(RbBaseCommand):
- program = "rb_id2str"
-
- help_string = "convert and print a Ruby ID to a C string and print it to the LLDB console"
-
- def call(self, debugger, command, exe_ctx, result):
- global_symbols = self.target.FindFirstGlobalVariable("ruby_global_symbols")
-
- id_val = self.frame.EvaluateExpression(command).GetValueAsUnsigned()
- num = self.rb_id_to_serial(id_val)
-
- last_id = global_symbols.GetChildMemberWithName("last_id").GetValueAsUnsigned()
- ID_ENTRY_SIZE = 2
- ID_ENTRY_UNIT = int(self.target.FindFirstGlobalVariable("ID_ENTRY_UNIT").GetValue())
-
- ids = global_symbols.GetChildMemberWithName("ids")
-
- if num <= last_id:
- idx = num // ID_ENTRY_UNIT
- ary = self.rb_ary_entry(ids, idx, result)
- pos = (num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE
- id_str = self.rb_ary_entry(ary, pos, result)
-
- RbInspector(debugger, result, self.ruby_globals).inspect(id_str)
-
- def rb_id_to_serial(self, id_val):
- if id_val > self.ruby_globals["tLAST_OP_ID"]:
- return id_val >> self.ruby_globals["RUBY_ID_SCOPE_SHIFT"]
- else:
- return id_val
-
- def rb_ary_entry(self, ary, idx, result):
- tRArray = self.target.FindFirstType("struct RArray").GetPointerType()
- ary = ary.Cast(tRArray)
- flags = ary.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
-
- if flags & self.ruby_globals["RUBY_FL_USER1"]:
- ptr = ary.GetValueForExpressionPath("->as.ary")
- else:
- ptr = ary.GetValueForExpressionPath("->as.heap.ptr")
-
- ptr_addr = ptr.GetValueAsUnsigned() + (idx * ptr.GetType().GetByteSize())
- return self.target.CreateValueFromAddress("ary_entry[%d]" % idx, lldb.SBAddress(ptr_addr, self.target), ptr.GetType().GetPointeeType())
diff --git a/misc/lldb_rb/commands/rclass_ext_command.py b/misc/lldb_rb/commands/rclass_ext_command.py
deleted file mode 100644
index 8bae911457..0000000000
--- a/misc/lldb_rb/commands/rclass_ext_command.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from lldb_rb.rb_base_command import RbBaseCommand
-
-class RclassExtCommand(RbBaseCommand):
- program = "rclass_ext"
- help_string = "retrieves and prints the rb_classext_struct for the VALUE pointer passed in"
-
- def call(self, debugger, command, exe_ctx, result):
- uintptr_t = self.target.FindFirstType("uintptr_t")
- rclass_t = self.target.FindFirstType("struct RClass")
- rclass_ext_t = self.target.FindFirstType("rb_classext_t")
-
- rclass_addr = self.target.EvaluateExpression(command).Cast(uintptr_t)
- rclass_ext_addr = (rclass_addr.GetValueAsUnsigned() + rclass_t.GetByteSize())
- debugger.HandleCommand("p *(rb_classext_t *)%0#x" % rclass_ext_addr)
diff --git a/misc/lldb_rb/commands/rp_command.py b/misc/lldb_rb/commands/rp_command.py
deleted file mode 100644
index 06b2516d50..0000000000
--- a/misc/lldb_rb/commands/rp_command.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import lldb
-
-from lldb_rb.constants import *
-from lldb_rb.utils import *
-from lldb_rb.rb_base_command import RbBaseCommand
-
-class RbID2StrCommand(RbBaseCommand):
- program = "rp"
-
- help_string = "convert and print a Ruby ID to a C string and print it to the LLDB console"
-
- def call(self, debugger, command, exe_ctx, result):
- val = self.frame.EvaluateExpression(command)
- inspector = RbInspector(debugger, result, self.ruby_globals)
- inspector.inspect(val)
diff --git a/misc/lldb_rb/constants.py b/misc/lldb_rb/constants.py
deleted file mode 100644
index 9cd56eccb0..0000000000
--- a/misc/lldb_rb/constants.py
+++ /dev/null
@@ -1,6 +0,0 @@
-HEAP_PAGE_ALIGN_LOG = 16
-HEAP_PAGE_ALIGN_MASK = (~(~0 << HEAP_PAGE_ALIGN_LOG))
-HEAP_PAGE_ALIGN = (1 << HEAP_PAGE_ALIGN_LOG)
-HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN
-
-IMEMO_MASK = 0x0F
diff --git a/misc/lldb_rb/lldb_interface.py b/misc/lldb_rb/lldb_interface.py
deleted file mode 100644
index 785a54b3e3..0000000000
--- a/misc/lldb_rb/lldb_interface.py
+++ /dev/null
@@ -1,7 +0,0 @@
-class LLDBInterface:
- def build_environment(self, debugger):
- self.debugger = debugger
- self.target = debugger.GetSelectedTarget()
- self.process = self.target.GetProcess()
- self.thread = self.process.GetSelectedThread()
- self.frame = self.thread.GetSelectedFrame()
diff --git a/misc/lldb_rb/rb_base_command.py b/misc/lldb_rb/rb_base_command.py
deleted file mode 100644
index 70a5addd6d..0000000000
--- a/misc/lldb_rb/rb_base_command.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import lldb
-from pydoc import locate
-from lldb_rb.constants import *
-from lldb_rb.utils import *
-
-class RbBaseCommand(LLDBInterface):
- @classmethod
- def register_lldb_command(cls, debugger, module_name):
- # Add any commands contained in this module to LLDB
- command = f"command script add -c {module_name}.{cls.__name__} {cls.program}"
- debugger.HandleCommand(command)
-
- @classmethod
- def lldb_init(cls, debugger):
- target = debugger.GetSelectedTarget()
- global SIZEOF_VALUE
- SIZEOF_VALUE = target.FindFirstType("VALUE").GetByteSize()
-
- value_types = []
- g = globals()
-
- imemo_types = target.FindFirstType("enum imemo_type")
-
- #for member in imemo_types.GetEnumMembers():
- # g[member.GetName()] = member.GetValueAsUnsigned()
-
- for enum in target.FindFirstGlobalVariable("ruby_dummy_gdb_enums"):
- enum = enum.GetType()
- members = enum.GetEnumMembers()
- for i in range(0, members.GetSize()):
- member = members.GetTypeEnumMemberAtIndex(i)
- name = member.GetName()
- value = member.GetValueAsUnsigned()
- g[name] = value
-
- if name.startswith("RUBY_T_"):
- value_types.append(name)
- g["value_types"] = value_types
- return g
-
- def __init__(self, debugger, _internal_dict):
- self.ruby_globals = RbBaseCommand.lldb_init(debugger)
- self.internal_dict = _internal_dict
-
- def __call__(self, debugger, command, exe_ctx, result):
- self.ruby_globals = RbBaseCommand.lldb_init(debugger)
- self.build_environment(debugger)
- self.call(debugger, command, exe_ctx, result)
-
- def call(self, debugger, command, exe_ctx, result):
- raise NotImplementedError("subclasses must implement call")
-
- def get_short_help(self):
- return self.__class__.help_string
-
- def get_long_help(self):
- return self.__class__.help_string
diff --git a/misc/lldb_rb/rb_heap_structs.py b/misc/lldb_rb/rb_heap_structs.py
deleted file mode 100644
index 86b38dbbbd..0000000000
--- a/misc/lldb_rb/rb_heap_structs.py
+++ /dev/null
@@ -1,143 +0,0 @@
-import lldb
-from lldb_rb.lldb_interface import LLDBInterface
-from lldb_rb.constants import *
-
-class HeapPage(LLDBInterface):
- def __init__(self, debugger, val):
- self.build_environment(debugger)
- self.page_type = self.target.FindFirstType("struct heap_page").GetPointerType()
- self.val = val
-
- def heap_page_body(self, command, ctx, result, internal_dict):
- process = self.target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
-
- val = frame.EvaluateExpression(command)
- page = self.get_page_body(val)
- print("Page body address: ", page.GetAddress(), file=result)
- print(page, file=result)
-
- def get_page_body(self, val):
- tHeapPageBody = self.target.FindFirstType("struct heap_page_body")
- addr = val.GetValueAsUnsigned()
- page_addr = addr & ~(HEAP_PAGE_ALIGN_MASK)
- address = lldb.SBAddress(page_addr, self.target)
- return self.target.CreateValueFromAddress("page", address, tHeapPageBody)
-
- def get_page_raw(self, val):
- body = self.get_page_body(val)
- return body.GetValueForExpressionPath("->header.page")
-
- def to_heap_page_struct(self):
- pagePtr = self.get_page_raw(self.val)
- return pagePtr.Cast(self.page_type)
-
-
-class RbObject(LLDBInterface):
- def __init__(self, ptr, debugger, ruby_globals):
- self.build_environment(debugger)
- self.ruby_globals = ruby_globals
-
- self.flUser1 = self.ruby_globals["RUBY_FL_USER1"]
- self.flUser2 = self.ruby_globals["RUBY_FL_USER2"]
- self.flUser3 = self.ruby_globals["RUBY_FL_USER3"]
- self.flUser4 = self.ruby_globals["RUBY_FL_USER4"]
- self.flUser5 = self.ruby_globals["RUBY_FL_USER5"]
- self.flUser6 = self.ruby_globals["RUBY_FL_USER6"]
- self.flUser7 = self.ruby_globals["RUBY_FL_USER7"]
- self.flUser8 = self.ruby_globals["RUBY_FL_USER8"]
- self.flUser9 = self.ruby_globals["RUBY_FL_USER9"]
- self.flUshift = self.ruby_globals["RUBY_FL_USHIFT"]
-
- self.tRBasic = self.target.FindFirstType("struct RBasic").GetPointerType()
- self.tRValue = self.target.FindFirstType("struct RVALUE")
-
- self.val = ptr.Cast(self.tRBasic)
- self.page = HeapPage(self.debugger, self.val)
- self.flags = self.val.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
-
- self.type = None
- self.type_name = ""
-
- def check_bits(self, bitmap_name, bitmap_index, bitmap_bit, v):
- page = self.page.to_heap_page_struct()
- bits = page.GetChildMemberWithName(bitmap_name)
- plane = bits.GetChildAtIndex(bitmap_index).GetValueAsUnsigned()
- if (plane & bitmap_bit) != 0:
- return v
- else:
- return ' '
-
- def dump_bits(self, result, end = "\n"):
- tRValue = self.target.FindFirstType("struct RVALUE")
- tUintPtr = self.target.FindFirstType("uintptr_t") # bits_t
-
- num_in_page = (self.val.GetValueAsUnsigned() & HEAP_PAGE_ALIGN_MASK) // tRValue.GetByteSize();
- bits_bitlength = tUintPtr.GetByteSize() * 8
- bitmap_index = num_in_page // bits_bitlength
- bitmap_offset = num_in_page & (bits_bitlength - 1)
- bitmap_bit = 1 << bitmap_offset
-
- page = self.page.to_heap_page_struct()
- print("bits: [%s%s%s%s%s]" % (
- self.check_bits("uncollectible_bits", bitmap_index, bitmap_bit, "L"),
- self.check_bits("mark_bits", bitmap_index, bitmap_bit, "M"),
- self.check_bits("pinned_bits", bitmap_index, bitmap_bit, "P"),
- self.check_bits("marking_bits", bitmap_index, bitmap_bit, "R"),
- self.check_bits("wb_unprotected_bits", bitmap_index, bitmap_bit, "U"),
- ), end=end, file=result)
-
- def promoted_p(self):
- rbFlPromoted = self.ruby_globals["RUBY_FL_PROMOTED"]
- return (self.flags & rbFlPromoted) == rbFlPromoted
-
- def frozen_p(self):
- rbFlFreeze = self.ruby_globals["RUBY_FL_FREEZE"]
- return (self.flags & rbFlFreeze) == rbFlFreeze
-
- def is_type(self, type_name):
- if self.type is None:
- flTMask = self.ruby_globals["RUBY_T_MASK"]
- flType = self.flags & flTMask
- self.type = flType
-
- if self.type == self.ruby_globals[type_name]:
- self.type_name = type_name
- return True
- else:
- return False
-
- def as_type(self, type_name):
- return self.val.Cast(self.tRValue.GetPointerType()).GetValueForExpressionPath("->as."+type_name)
-
- def ary_ptr(self):
- rval = self.as_type("array")
- if self.flags & self.ruby_globals["RUBY_FL_USER1"]:
- ptr = rval.GetValueForExpressionPath("->as.ary")
- else:
- ptr = rval.GetValueForExpressionPath("->as.heap.ptr")
- return ptr
-
- def ary_len(self):
- if self.flags & self.flUser1:
- len = ((self.flags &
- (self.flUser3 | self.flUser4 | self.flUser5 | self.flUser6 |
- self.flUser7 | self.flUser8 | self.flUser9)
- ) >> (self.flUshift + 3))
- else:
- rval = self.as_type("array")
- len = rval.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
-
- return len
-
- def bignum_len(self):
- if self.flags & self.flUser2:
- len = ((self.flags &
- (self.flUser3 | self.flUser4 | self.flUser5)
- ) >> (self.flUshift + 3))
- else:
- len = (self.as_type("bignum").GetValueForExpressionPath("->as.heap.len").
- GetValueAsUnsigned())
-
- return len
diff --git a/misc/lldb_rb/utils.py b/misc/lldb_rb/utils.py
deleted file mode 100644
index a6bbd385cd..0000000000
--- a/misc/lldb_rb/utils.py
+++ /dev/null
@@ -1,513 +0,0 @@
-from lldb_rb.lldb_interface import LLDBInterface
-from lldb_rb.rb_heap_structs import HeapPage, RbObject
-from lldb_rb.constants import *
-
-class RbInspector(LLDBInterface):
- def __init__(self, debugger, result, ruby_globals):
- self.build_environment(debugger)
- self.result = result
- self.ruby_globals = ruby_globals
-
- def _append_command_output(self, command):
- output1 = self.result.GetOutput()
- self.debugger.GetCommandInterpreter().HandleCommand(command, self.result)
- output2 = self.result.GetOutput()
- self.result.Clear()
- self.result.write(output1)
- self.result.write(output2)
-
- def _append_expression(self, expression):
- self._append_command_output("expression " + expression)
-
- def string2cstr(self, rstring):
- """Returns the pointer to the C-string in the given String object"""
- if rstring.TypeIsPointerType():
- rstring = rstring.Dereference()
-
- flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned
- clen = int(rstring.GetValueForExpressionPath(".len").value, 0)
- if flags & self.ruby_globals["RUBY_FL_USER1"]:
- cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0)
- else:
- cptr = int(rstring.GetValueForExpressionPath(".as.embed.ary").location, 0)
-
- return cptr, clen
-
- def output_string(self, rstring):
- cptr, clen = self.string2cstr(rstring)
- self._append_expression("*(const char (*)[%d])%0#x" % (clen, cptr))
-
- def fixnum_p(self, x):
- return x & self.ruby_globals["RUBY_FIXNUM_FLAG"] != 0
-
- def flonum_p(self, x):
- return (x & self.ruby_globals["RUBY_FLONUM_MASK"]) == self.ruby_globals["RUBY_FLONUM_FLAG"]
-
- def static_sym_p(self, x):
- special_shift = self.ruby_globals["RUBY_SPECIAL_SHIFT"]
- symbol_flag = self.ruby_globals["RUBY_SYMBOL_FLAG"]
- return (x & ~(~0 << special_shift)) == symbol_flag
-
- def generic_inspect(self, val, rtype):
- tRType = self.target.FindFirstType("struct %s" % rtype).GetPointerType()
- val = val.Cast(tRType)
- self._append_expression("*(struct %s *) %0#x" % (rtype, val.GetValueAsUnsigned()))
-
- def inspect(self, val):
- rbTrue = self.ruby_globals["RUBY_Qtrue"]
- rbFalse = self.ruby_globals["RUBY_Qfalse"]
- rbNil = self.ruby_globals["RUBY_Qnil"]
- rbUndef = self.ruby_globals["RUBY_Qundef"]
- rbImmediateMask = self.ruby_globals["RUBY_IMMEDIATE_MASK"]
-
- if self.inspect_node(val):
- return
-
- num = val.GetValueAsSigned()
- if num == rbFalse:
- print('false', file=self.result)
- elif num == rbTrue:
- print('true', file=self.result)
- elif num == rbNil:
- print('nil', file=self.result)
- elif num == rbUndef:
- print('undef', file=self.result)
- elif self.fixnum_p(num):
- print(num >> 1, file=self.result)
- elif self.flonum_p(num):
- self._append_expression("rb_float_value(%0#x)" % val.GetValueAsUnsigned())
- elif self.static_sym_p(num):
- if num < 128:
- print("T_SYMBOL: %c" % num, file=self.result)
- else:
- print("T_SYMBOL: (%x)" % num, file=self.result)
- self._append_expression("rb_id2name(%0#x)" % (num >> 8))
-
- elif num & rbImmediateMask:
- print('immediate(%x)' % num, file=self.result)
- else:
- rval = RbObject(val, self.debugger, self.ruby_globals)
- rval.dump_bits(self.result)
-
- flaginfo = ""
- if rval.promoted_p():
- flaginfo += "[PROMOTED] "
- if rval.frozen_p():
- flaginfo += "[FROZEN] "
-
- if rval.is_type("RUBY_T_NONE"):
- print('T_NONE: %s%s' % (flaginfo, val.Dereference()), file=self.result)
-
- elif rval.is_type("RUBY_T_NIL"):
- print('T_NIL: %s%s' % (flaginfo, val.Dereference()), file=self.result)
-
- elif rval.is_type("RUBY_T_OBJECT"):
- self.result.write('T_OBJECT: %s' % flaginfo)
- self._append_expression("*(struct RObject*)%0#x" % val.GetValueAsUnsigned())
-
- elif (rval.is_type("RUBY_T_CLASS") or
- rval.is_type("RUBY_T_MODULE") or
- rval.is_type("RUBY_T_ICLASS")):
- self.result.write('T_%s: %s' % (rval.type_name.split('_')[-1], flaginfo))
- tRClass = self.target.FindFirstType("struct RClass")
-
- self._append_expression("*(struct RClass*)%0#x" % val.GetValueAsUnsigned())
- if not val.Cast(tRClass).GetChildMemberWithName("ptr").IsValid():
- self._append_expression(
- "*(struct rb_classext_struct*)%0#x" %
- (val.GetValueAsUnsigned() + tRClass.GetByteSize())
- )
-
- elif rval.is_type("RUBY_T_STRING"):
- self.result.write('T_STRING: %s' % flaginfo)
- tRString = self.target.FindFirstType("struct RString").GetPointerType()
-
- chilled = self.ruby_globals["RUBY_FL_USER3"]
- if (rval.flags & chilled) != 0:
- self.result.write("[CHILLED] ")
-
- rb_enc_mask = self.ruby_globals["RUBY_ENCODING_MASK"]
- rb_enc_shift = self.ruby_globals["RUBY_ENCODING_SHIFT"]
- encidx = ((rval.flags & rb_enc_mask) >> rb_enc_shift)
- encname = self.target.FindFirstType("enum ruby_preserved_encindex") \
- .GetEnumMembers().GetTypeEnumMemberAtIndex(encidx) \
- .GetName()
-
- if encname is not None:
- self.result.write('[%s] ' % encname[14:])
- else:
- self.result.write('[enc=%d] ' % encidx)
-
- coderange = rval.flags & self.ruby_globals["RUBY_ENC_CODERANGE_MASK"]
- if coderange == self.ruby_globals["RUBY_ENC_CODERANGE_7BIT"]:
- self.result.write('[7BIT] ')
- elif coderange == self.ruby_globals["RUBY_ENC_CODERANGE_VALID"]:
- self.result.write('[VALID] ')
- elif coderange == self.ruby_globals["RUBY_ENC_CODERANGE_BROKEN"]:
- self.result.write('[BROKEN] ')
- else:
- self.result.write('[UNKNOWN] ')
-
- ptr, len = self.string2cstr(val.Cast(tRString))
- if len == 0:
- self.result.write("(empty)\n")
- else:
- self._append_expression("*(const char (*)[%d])%0#x" % (len, ptr))
-
- elif rval.is_type("RUBY_T_SYMBOL"):
- self.result.write('T_SYMBOL: %s' % flaginfo)
- tRSymbol = self.target.FindFirstType("struct RSymbol").GetPointerType()
- tRString = self.target.FindFirstType("struct RString").GetPointerType()
-
- val = val.Cast(tRSymbol)
- self._append_expression('(ID)%0#x ' % val.GetValueForExpressionPath("->id").GetValueAsUnsigned())
- self.output_string(val.GetValueForExpressionPath("->fstr").Cast(tRString))
-
- elif rval.is_type("RUBY_T_ARRAY"):
- len = rval.ary_len()
- ptr = rval.ary_ptr()
-
- self.result.write("T_ARRAY: %slen=%d" % (flaginfo, len))
-
- if rval.flags & self.ruby_globals["RUBY_FL_USER1"]:
- self.result.write(" (embed)")
- elif rval.flags & self.ruby_globals["RUBY_FL_USER2"]:
- shared = val.GetValueForExpressionPath("->as.heap.aux.shared").GetValueAsUnsigned()
- self.result.write(" (shared) shared=%016x" % shared)
- else:
- capa = val.GetValueForExpressionPath("->as.heap.aux.capa").GetValueAsSigned()
- self.result.write(" (ownership) capa=%d" % capa)
- if len == 0:
- self.result.write(" {(empty)}\n")
- else:
- self.result.write("\n")
- if ptr.GetValueAsSigned() == 0:
- self._append_expression("-fx -- ((struct RArray*)%0#x)->as.ary" % val.GetValueAsUnsigned())
- else:
- self._append_expression("-Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()))
-
- elif rval.is_type("RUBY_T_HASH"):
- self.result.write("T_HASH: %s" % flaginfo)
- ptr = val.GetValueAsUnsigned()
- self._append_expression("*(struct RHash *) %0#x" % ptr)
- if rval.flags & self.ruby_globals["RUBY_FL_USER3"]:
- self._append_expression("*(struct st_table *) (%0#x + sizeof(struct RHash))" % ptr)
- else:
- self._append_expression("*(struct ar_table *) (%0#x + sizeof(struct RHash))" % ptr)
-
- elif rval.is_type("RUBY_T_BIGNUM"):
- sign = '-'
- if (rval.flags & self.ruby_globals["RUBY_FL_USER1"]) != 0:
- sign = '+'
- len = rval.bignum_len()
-
- if rval.flags & self.ruby_globals["RUBY_FL_USER2"]:
- print("T_BIGNUM: sign=%s len=%d (embed)" % (sign, len), file=self.result)
- self._append_expression("((struct RBignum *) %0#x)->as.ary"
- % val.GetValueAsUnsigned())
- else:
- print("T_BIGNUM: sign=%s len=%d" % (sign, len), file=self.result)
- print(rval.as_type("bignum"), file=self.result)
- self._append_expression("-Z %d -fx -- ((struct RBignum*)%d)->as.heap.digits" %
- (len, val.GetValueAsUnsigned()))
-
- elif rval.is_type("RUBY_T_FLOAT"):
- self._append_expression("((struct RFloat *)%d)->float_value"
- % val.GetValueAsUnsigned())
-
- elif rval.is_type("RUBY_T_RATIONAL"):
- tRRational = self.target.FindFirstType("struct RRational").GetPointerType()
- val = val.Cast(tRRational)
- self.inspect(val.GetValueForExpressionPath("->num"))
- output = self.result.GetOutput()
- self.result.Clear()
- self.result.write("(Rational) " + output.rstrip() + " / ")
- self.inspect(val.GetValueForExpressionPath("->den"))
-
- elif rval.is_type("RUBY_T_COMPLEX"):
- tRComplex = self.target.FindFirstType("struct RComplex").GetPointerType()
- val = val.Cast(tRComplex)
- self.inspect(val.GetValueForExpressionPath("->real"))
- real = self.result.GetOutput().rstrip()
- self.result.Clear()
- self.inspect(val.GetValueForExpressionPath("->imag"))
- imag = self.result.GetOutput().rstrip()
- self.result.Clear()
- if not imag.startswith("-"):
- imag = "+" + imag
- print("(Complex) " + real + imag + "i", file=self.result)
-
- elif rval.is_type("RUBY_T_REGEXP"):
- tRRegex = self.target.FindFirstType("struct RRegexp").GetPointerType()
- val = val.Cast(tRRegex)
- print("(Regex) ->src {", file=self.result)
- self.inspect(val.GetValueForExpressionPath("->src"))
- print("}", file=self.result)
-
- elif rval.is_type("RUBY_T_DATA"):
- tRTypedData = self.target.FindFirstType("struct RTypedData").GetPointerType()
- val = val.Cast(tRTypedData)
- flag = val.GetValueForExpressionPath("->typed_flag")
-
- if flag.GetValueAsUnsigned() == 1:
- print("T_DATA: %s" %
- val.GetValueForExpressionPath("->type->wrap_struct_name"),
- file=self.result)
- self._append_expression("*(struct RTypedData *) %0#x" % val.GetValueAsUnsigned())
- else:
- print("T_DATA:", file=self.result)
- self._append_expression("*(struct RData *) %0#x" % val.GetValueAsUnsigned())
-
- elif rval.is_type("RUBY_T_IMEMO"):
- imemo_type = ((rval.flags >> self.ruby_globals["RUBY_FL_USHIFT"])
- & IMEMO_MASK)
- print("T_IMEMO: ", file=self.result)
-
- self._append_expression("(enum imemo_type) %d" % imemo_type)
- self._append_expression("*(struct MEMO *) %0#x" % val.GetValueAsUnsigned())
-
- elif rval.is_type("RUBY_T_FILE"):
- self.generic_inspect(val, "RFile")
-
- elif rval.is_type("RUBY_T_MOVED"):
- self.generic_inspect(val, "RMoved")
-
- elif rval.is_type("RUBY_T_MATCH"):
- self.generic_inspect(val, "RMatch")
-
- elif rval.is_type("RUBY_T_STRUCT"):
- self.generic_inspect(val, "RStruct")
-
- elif rval.is_type("RUBY_T_ZOMBIE"):
- self.generic_inspect(val, "RZombie")
-
- else:
- print("Not-handled type %0#x" % rval.type, file=self.result)
- print(val, file=self.result)
-
- def inspect_node(self, val):
- tRNode = self.target.FindFirstType("struct RNode").GetPointerType()
-
- # if val.GetType() != tRNode: does not work for unknown reason
-
- if val.GetType().GetPointeeType().name != "NODE":
- return False
-
- rbNodeTypeMask = self.ruby_globals["RUBY_NODE_TYPEMASK"]
- rbNodeTypeShift = self.ruby_globals["RUBY_NODE_TYPESHIFT"]
- flags = val.Cast(tRNode).GetChildMemberWithName("flags").GetValueAsUnsigned()
- nd_type = (flags & rbNodeTypeMask) >> rbNodeTypeShift
-
- self._append_expression("(node_type) %d" % nd_type)
-
- if nd_type == self.ruby_globals["NODE_SCOPE"]:
- self._append_expression("*(rb_node_scope_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_BLOCK"]:
- self._append_expression("*(rb_node_block_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_IF"]:
- self._append_expression("*(rb_node_if_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_UNLESS"]:
- self._append_expression("*(rb_node_unless_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CASE"]:
- self._append_expression("*(rb_node_case_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CASE2"]:
- self._append_expression("*(rb_node_case2_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CASE3"]:
- self._append_expression("*(rb_node_case3_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_WHEN"]:
- self._append_expression("*(rb_node_when_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_IN"]:
- self._append_expression("*(rb_node_in_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_WHILE"]:
- self._append_expression("*(rb_node_while_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_UNTIL"]:
- self._append_expression("*(rb_node_until_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ITER"]:
- self._append_expression("*(rb_node_iter_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FOR"]:
- self._append_expression("*(rb_node_for_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FOR_MASGN"]:
- self._append_expression("*(rb_node_for_masgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_BREAK"]:
- self._append_expression("*(rb_node_break_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_NEXT"]:
- self._append_expression("*(rb_node_next_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_REDO"]:
- self._append_expression("*(rb_node_redo_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_RETRY"]:
- self._append_expression("*(rb_node_retry_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_BEGIN"]:
- self._append_expression("*(rb_node_begin_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_RESCUE"]:
- self._append_expression("*(rb_node_rescue_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_RESBODY"]:
- self._append_expression("*(rb_node_resbody_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ENSURE"]:
- self._append_expression("*(rb_node_ensure_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_AND"]:
- self._append_expression("*(rb_node_and_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OR"]:
- self._append_expression("*(rb_node_or_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_MASGN"]:
- self._append_expression("*(rb_node_masgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_LASGN"]:
- self._append_expression("*(rb_node_lasgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DASGN"]:
- self._append_expression("*(rb_node_dasgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_GASGN"]:
- self._append_expression("*(rb_node_gasgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_IASGN"]:
- self._append_expression("*(rb_node_iasgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CDECL"]:
- self._append_expression("*(rb_node_cdecl_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CVASGN"]:
- self._append_expression("*(rb_node_cvasgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OP_ASGN1"]:
- self._append_expression("*(rb_node_op_asgn1_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OP_ASGN2"]:
- self._append_expression("*(rb_node_op_asgn2_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OP_ASGN_AND"]:
- self._append_expression("*(rb_node_op_asgn_and_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OP_ASGN_OR"]:
- self._append_expression("*(rb_node_op_asgn_or_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OP_CDECL"]:
- self._append_expression("*(rb_node_op_cdecl_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CALL"]:
- self._append_expression("*(rb_node_call_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OPCALL"]:
- self._append_expression("*(rb_node_opcall_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FCALL"]:
- self._append_expression("*(rb_node_fcall_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_VCALL"]:
- self._append_expression("*(rb_node_vcall_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_QCALL"]:
- self._append_expression("*(rb_node_qcall_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_SUPER"]:
- self._append_expression("*(rb_node_super_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ZSUPER"]:
- self._append_expression("*(rb_node_zsuper_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_LIST"]:
- self._append_expression("*(rb_node_list_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ZLIST"]:
- self._append_expression("*(rb_node_zlist_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_HASH"]:
- self._append_expression("*(rb_node_hash_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_RETURN"]:
- self._append_expression("*(rb_node_return_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_YIELD"]:
- self._append_expression("*(rb_node_yield_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_LVAR"]:
- self._append_expression("*(rb_node_lvar_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DVAR"]:
- self._append_expression("*(rb_node_dvar_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_GVAR"]:
- self._append_expression("*(rb_node_gvar_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CONST"]:
- self._append_expression("*(rb_node_const_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CVAR"]:
- self._append_expression("*(rb_node_cvar_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_NTH_REF"]:
- self._append_expression("*(rb_node_nth_ref_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_BACK_REF"]:
- self._append_expression("*(rb_node_back_ref_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_MATCH"]:
- self._append_expression("*(rb_node_match_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_MATCH2"]:
- self._append_expression("*(rb_node_match2_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_MATCH3"]:
- self._append_expression("*(rb_node_match3_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_STR"]:
- self._append_expression("*(rb_node_str_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DSTR"]:
- self._append_expression("*(rb_node_dstr_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_XSTR"]:
- self._append_expression("*(rb_node_xstr_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DXSTR"]:
- self._append_expression("*(rb_node_dxstr_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_EVSTR"]:
- self._append_expression("*(rb_node_evstr_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_REGX"]:
- self._append_expression("*(rb_node_regx_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DREGX"]:
- self._append_expression("*(rb_node_dregx_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ONCE"]:
- self._append_expression("*(rb_node_once_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ARGS"]:
- self._append_expression("*(rb_node_args_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ARGS_AUX"]:
- self._append_expression("*(rb_node_args_aux_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_OPT_ARG"]:
- self._append_expression("*(rb_node_opt_arg_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_KW_ARG"]:
- self._append_expression("*(rb_node_kw_arg_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_POSTARG"]:
- self._append_expression("*(rb_node_postarg_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ARGSCAT"]:
- self._append_expression("*(rb_node_argscat_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ARGSPUSH"]:
- self._append_expression("*(rb_node_argspush_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_SPLAT"]:
- self._append_expression("*(rb_node_splat_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DEFN"]:
- self._append_expression("*(rb_node_defn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DEFS"]:
- self._append_expression("*(rb_node_defs_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ALIAS"]:
- self._append_expression("*(rb_node_alias_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_VALIAS"]:
- self._append_expression("*(rb_node_valias_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_UNDEF"]:
- self._append_expression("*(rb_node_undef_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_CLASS"]:
- self._append_expression("*(rb_node_class_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_MODULE"]:
- self._append_expression("*(rb_node_module_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_SCLASS"]:
- self._append_expression("*(rb_node_sclass_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_COLON2"]:
- self._append_expression("*(rb_node_colon2_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_COLON3"]:
- self._append_expression("*(rb_node_colon3_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DOT2"]:
- self._append_expression("*(rb_node_dot2_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DOT3"]:
- self._append_expression("*(rb_node_dot3_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FLIP2"]:
- self._append_expression("*(rb_node_flip2_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FLIP3"]:
- self._append_expression("*(rb_node_flip3_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_SELF"]:
- self._append_expression("*(rb_node_self_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_NIL"]:
- self._append_expression("*(rb_node_nil_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_TRUE"]:
- self._append_expression("*(rb_node_true_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FALSE"]:
- self._append_expression("*(rb_node_false_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ERRINFO"]:
- self._append_expression("*(rb_node_errinfo_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DEFINED"]:
- self._append_expression("*(rb_node_defined_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_POSTEXE"]:
- self._append_expression("*(rb_node_postexe_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_DSYM"]:
- self._append_expression("*(rb_node_dsym_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ATTRASGN"]:
- self._append_expression("*(rb_node_attrasgn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_LAMBDA"]:
- self._append_expression("*(rb_node_lambda_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ARYPTN"]:
- self._append_expression("*(rb_node_aryptn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_HSHPTN"]:
- self._append_expression("*(rb_node_hshptn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FNDPTN"]:
- self._append_expression("*(rb_node_fndptn_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_ERROR"]:
- self._append_expression("*(rb_node_error_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_LINE"]:
- self._append_expression("*(rb_node_line_t *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_FILE"]:
- self._append_expression("*(rb_node_file_t *) %0#x" % val.GetValueAsUnsigned())
- else:
- self._append_expression("*(NODE *) %0#x" % val.GetValueAsUnsigned())
- return True
diff --git a/misc/rb_optparse.bash b/misc/rb_optparse.bash
index f77d937c87..8a59ec2dda 100644
--- a/misc/rb_optparse.bash
+++ b/misc/rb_optparse.bash
@@ -1,5 +1,4 @@
-# -*- bash -*-
-#
+#! /bin/bash
# Completion for bash:
#
# (1) install this file,
@@ -17,5 +16,5 @@ _rb_optparse() {
}
rb_optparse () {
- [ $# = 0 ] || complete -o default -F _rb_optparse "$@"
+ [ $# = 0 ] || complete -o default -F _rb_optparse "$@"
}
diff --git a/misc/rb_optparse.zsh b/misc/rb_optparse.zsh
index 258d4f856c..d53170c5f7 100644..100755
--- a/misc/rb_optparse.zsh
+++ b/misc/rb_optparse.zsh
@@ -1,5 +1,4 @@
-# -*- zsh -*-
-#
+#!/bin/zsh
# Completion for zsh:
# (based on <http://d.hatena.ne.jp/rubikitch/20071002/zshcomplete>)
#
@@ -8,13 +7,13 @@
# cp rb_optparse.zsh ~/.zsh.d/rb_optparse.zsh
#
# (2) load the script, and add a directory to fpath before compinit.
-# echo '. ~/.zsh.d/rb_optparse.zsh' >> "${ZDOTDIR:-~}/.zshrc"
-# echo 'fpath=(~/.zsh.d/Completion $fpath)' >> "${ZDOTDIR:-~}/.zshrc"
-# echo 'autoload -U compinit; compinit' >> "${ZDOTDIR:-~}/.zshrc"
+# echo '. ~/.zsh.d/rb_optparse.zsh' >> ~/.zshrc
+# echo 'fpath=(~/.zsh.d/Completion $fpath)' >> ~/.zshrc
+# echo 'autoload -U compinit; compinit' >> ~/.zshrc
#
# (3) restart zsh.
#
-# (4) generate completion files once.
+# (4) geneate completion files once.
# generate-complete-function/ruby/optparse COMMAND1
# generate-complete-function/ruby/optparse COMMAND2
#
@@ -25,8 +24,8 @@ generate-complete-function/ruby/optparse ()
mkdir -p "${ZSH_COMPLETION_DIR-$HOME/.zsh.d/Completion}"
$1 "--*-completion-zsh=${1:t}" >! "${ZSH_COMPLETION_DIR-$HOME/.zsh.d/Completion}/$cmpl"
if [[ $(type -w "$cmpl") == "${cmpl}: function" ]]; then
- unfunction "$cmpl"
- autoload -U "$cmpl"
+ unfunction "$cmpl"
+ autoload -U "$cmpl"
else
compinit "$cmpl"
fi
diff --git a/misc/rdoc-mode.el b/misc/rdoc-mode.el
new file mode 100644
index 0000000000..c26c2ee564
--- /dev/null
+++ b/misc/rdoc-mode.el
@@ -0,0 +1,166 @@
+;;
+;; rdoc-mode.el
+;; Major mode for RDoc editing
+;;
+
+;; Created: Fri Sep 18 09:04:49 JST 2009
+
+;; License: Ruby's
+
+(require 'derived)
+
+;;;###autoload
+(define-derived-mode rdoc-mode text-mode "RDoc"
+ "Major mode for RD editing.
+\\{rdoc-mode-map}"
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate "^\\(=+\\|\\*+\\)[ \t\v\f]*\\|^\\s *$")
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start paragraph-separate)
+ (make-local-variable 'require-final-newline)
+ (setq require-final-newline t)
+ (make-local-variable 'font-lock-defaults)
+ (setq font-lock-defaults '((rdoc-font-lock-keywords) t nil))
+ (make-local-variable 'font-lock-keywords)
+ (setq font-lock-keywords rdoc-font-lock-keywords)
+ (make-local-variable 'outline-regexp)
+ (setq outline-regexp "^\\(=+\\)[ \t\v\f]*")
+ (outline-minor-mode t)
+ (setq show-trailing-whitespace t)
+ (rdoc-setup-keys)
+ (setq indent-tabs-mode nil)
+ (run-hooks 'rdoc-mode-hook)
+ )
+
+(defun rdoc-fill-paragraph (&optional justify region)
+ "Fills paragraph, except for cited region"
+ (interactive (progn
+ (barf-if-buffer-read-only)
+ (list (if current-prefix-arg 'full))))
+ (save-excursion
+ (beginning-of-line)
+ (save-restriction
+ (let ((pos (point)) beg end indent hanging)
+ (cond
+ ((looking-at "^ +\\(\\*\\s *\\)")
+ (setq indent (- (match-end 0) (match-beginning 0))
+ hanging (- (match-end 1) (match-beginning 1))))
+ ((looking-at "^ +")
+ (setq indent (- (match-end 0) (match-beginning 0)))
+ (when (and (re-search-backward "^[^ ]\\|^\\( *\\(\\* *\\)\\)" nil t)
+ (match-beginning 1)
+ (= indent (- (match-end 1) (match-beginning 1))))
+ (setq hanging (- (match-end 2) (match-beginning 2)))
+ (setq beg (match-beginning 1))))
+ ((setq beg t)))
+ (when beg
+ (when indent
+ (goto-char pos)
+ (while (progn (beginning-of-line 2)
+ (and (looking-at "^\\( +\\)\\S ")
+ (= indent (- (match-end 1) (match-beginning 1))))))
+ (setq end (point))
+ (when (and beg (not region))
+ (setq region (list beg end))
+ (narrow-to-region beg end)
+ ))
+ (goto-char pos)
+ (fill-paragraph justify region)
+ (when (and indent
+ (or (goto-char beg) t)
+ (or (beginning-of-line 2) t)
+ (looking-at "^\\( +\\)")
+ (= (- indent hanging) (- (match-end 0) (match-beginning 0))))
+ (insert-char ?\s hanging)
+ (beginning-of-line)
+ (narrow-to-region (point) end)
+ (fill-paragraph justify (list (point) end))))))))
+
+(defun rdoc-setup-keys ()
+ (interactive)
+ (define-key rdoc-mode-map "\M-q" 'rdoc-fill-paragraph)
+ )
+
+(defvar rdoc-heading1-face 'font-lock-keywordoc-face)
+(defvar rdoc-heading2-face 'font-lock-type-face)
+(defvar rdoc-heading3-face 'font-lock-variable-name-face)
+(defvar rdoc-heading4-face 'font-lock-comment-face)
+(defvar rdoc-bold-face 'font-lock-function-name-face)
+(defvar rdoc-emphasis-face 'font-lock-function-name-face)
+(defvar rdoc-code-face 'font-lock-keyword-face)
+(defvar rdoc-description-face 'font-lock-constant-face)
+
+(defvar rdoc-font-lock-keywords
+ (list
+ (list "^=([^=\r\n].*)?$"
+ 0 rdoc-heading1-face)
+ (list "^==([^=\r\n].*)?$"
+ 0 rdoc-heading2-face)
+ (list "^===([^=\r\n].*)?$"
+ 0 rdoc-heading3-face)
+ (list "^====+.*$"
+ 0 rdoc-heading4-face)
+ (list "\\(^\\|[ \t\v\f]\\)\\(\\*\\(\\sw\\|[-_:]\\)+\\*\\)\\($\\|[ \t\v\f]\\)"
+ 2 rdoc-bold-face) ; *bold*
+ (list "\\(^\\|[ \t\v\f]\\)\\(_\\(\\sw\\|[-_:]\\)+_\\)\\($\\|[ \t\v\f]\\)"
+ 2 rdoc-emphasis-face) ; _emphasis_
+ (list "\\(^\\|[ \t\v\f]\\)\\(\\+\\(\\sw\\|[-_:]\\)+\\+\\)\\($\\|[ \t\v\f]\\)"
+ 2 rdoc-code-face) ; +code+
+ (list "<em>[^<>]*</em>" 0 rdoc-emphasis-face)
+ (list "<i>[^<>]*</i>" 0 rdoc-emphasis-face)
+ (list "<b>[^<>]*</b>" 0 rdoc-bold-face)
+ (list "<tt>[^<>]*</tt>" 0 rdoc-code-face)
+ (list "<code>[^<>]*</code>" 0 rdoc-code-face)
+ (list "^\\([-*]\\|[0-9]+\\.\\|[A-Za-z]\\.\\)\\s "
+ 1 rdoc-description-face) ; bullet | numbered | alphabetically numbered
+ (list "^\\[[^\]]*\\]\\|\\S .*::\\)\\([ \t\v\f]\\|$\\)"
+ 1 rdoc-description-face) ; labeled | node
+ ;(list "^[ \t\v\f]+\\(.*\\)" 1 rdoc-verbatim-face)
+ ))
+
+(defun rdoc-imenu-create-index ()
+ (let ((root '(nil . nil))
+ cur-alist
+ (cur-level 0)
+ (pattern (concat outline-regexp "\\(.*?\\)[ \t\v\f]*$"))
+ (empty-heading "-")
+ (self-heading ".")
+ pos level heading alist)
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward pattern (point-max) t)
+ (setq heading (match-string-no-properties 2)
+ level (min 6 (length (match-string-no-properties 1)))
+ pos (match-beginning 1))
+ (if (= (length heading) 0)
+ (setq heading empty-heading))
+ (setq alist (list (cons heading pos)))
+ (cond
+ ((= cur-level level) ; new sibling
+ (setcdr cur-alist alist)
+ (setq cur-alist alist))
+ ((< cur-level level) ; first child
+ (dotimes (i (- level cur-level 1))
+ (setq alist (list (cons empty-heading alist))))
+ (if cur-alist
+ (let* ((parent (car cur-alist))
+ (self-pos (cdr parent)))
+ (setcdr parent (cons (cons self-heading self-pos) alist)))
+ (setcdr root alist)) ; primogenitor
+ (setq cur-alist alist
+ cur-level level))
+ (t ; new sibling of an ancestor
+ (let ((sibling-alist (last (cdr root))))
+ (dotimes (i (1- level))
+ (setq sibling-alist (last (cdar sibling-alist))))
+ (setcdr sibling-alist alist)
+ (setq cur-alist alist
+ cur-level level))))))
+ (cdr root)))
+
+(defun rdoc-set-imenu-create-index-function ()
+ (setq imenu-create-index-function 'rdoc-imenu-create-index))
+
+(add-hook 'rdoc-mode-hook 'rdoc-set-imenu-create-index-function)
+
+(provide 'rdoc-mode)
diff --git a/misc/ruby-additional.el b/misc/ruby-additional.el
new file mode 100644
index 0000000000..432adfedb6
--- /dev/null
+++ b/misc/ruby-additional.el
@@ -0,0 +1,181 @@
+;;; ruby-additional.el --- ruby-mode extensions yet to be merged into Emacs
+
+;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada, Akinori MUSHA
+;; URL: https://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/misc/
+;; Created: 3 Sep 2012
+;; Package-Requires: ((emacs "24.3") (ruby-mode "1.2"))
+;; Keywords: ruby, languages
+
+;;; Commentary:
+;;
+;; This package contains ruby-mode extensions yet to be merged into
+;; the latest released version of Emacs distribution. For older
+;; versions of Emacs, use ruby-mode.el bundled with CRuby.
+
+;;; Code:
+
+(eval-when-compile
+ (require 'ruby-mode))
+
+(eval-after-load 'ruby-mode
+ '(progn
+ (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end)
+
+ (defun ruby-insert-end ()
+ (interactive)
+ (if (eq (char-syntax (preceding-char)) ?w)
+ (insert " "))
+ (insert "end")
+ (save-excursion
+ (if (eq (char-syntax (following-char)) ?w)
+ (insert " "))
+ (ruby-indent-line t)
+ (end-of-line)))
+
+ (defconst ruby-default-encoding-map
+ '((us-ascii . nil) ;; Do not put coding: us-ascii
+ (utf-8 . nil) ;; Do not put coding: utf-8
+ (shift-jis . cp932) ;; Emacs charset name of Shift_JIS
+ (shift_jis . cp932) ;; MIME charset name of Shift_JIS
+ (japanese-cp932 . cp932)) ;; Emacs charset name of CP932
+ )
+
+ (custom-set-default 'ruby-encoding-map ruby-default-encoding-map)
+
+ (defcustom ruby-encoding-map ruby-default-encoding-map
+ "Alist to map encoding name from Emacs to Ruby.
+Associating an encoding name with nil means it needs not be
+explicitly declared in magic comment."
+ :type '(repeat (cons (symbol :tag "From") (symbol :tag "To")))
+ :group 'ruby)
+
+ (defun ruby-mode-set-encoding ()
+ "Insert or update a magic comment header with the proper encoding.
+`ruby-encoding-map' is looked up to convert an encoding name from
+Emacs to Ruby."
+ (let* ((nonascii
+ (save-excursion
+ (widen)
+ (goto-char (point-min))
+ (re-search-forward "[^\0-\177]" nil t)))
+ (coding-system
+ (or coding-system-for-write
+ buffer-file-coding-system))
+ (coding-system
+ (and coding-system
+ (coding-system-change-eol-conversion coding-system nil)))
+ (coding-system
+ (and coding-system
+ (or
+ (coding-system-get coding-system :mime-charset)
+ (let ((coding-type (coding-system-get coding-system :coding-type)))
+ (cond ((eq coding-type 'undecided)
+ (if nonascii
+ (or (and (coding-system-get coding-system :prefer-utf-8)
+ 'utf-8)
+ (coding-system-get default-buffer-file-coding-system :coding-type)
+ 'ascii-8bit)))
+ ((memq coding-type '(utf-8 shift-jis))
+ coding-type)
+ (t coding-system))))))
+ (coding-system
+ (or coding-system
+ 'us-ascii))
+ (coding-system
+ (let ((cons (assq coding-system ruby-encoding-map)))
+ (if cons (cdr cons) coding-system)))
+ (coding-system
+ (and coding-system
+ (symbol-name coding-system))))
+ (if coding-system
+ (save-excursion
+ (widen)
+ (goto-char (point-min))
+ (if (looking-at "^#!") (beginning-of-line 2))
+ (cond ((looking-at "\\s *#.*-\*-\\s *\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)\\s *\\(;\\|-\*-\\)")
+ (unless (string= (match-string 2) coding-system)
+ (goto-char (match-beginning 2))
+ (delete-region (point) (match-end 2))
+ (and (looking-at "-\*-")
+ (let ((n (skip-chars-backward " ")))
+ (cond ((= n 0) (insert " ") (backward-char))
+ ((= n -1) (insert " "))
+ ((forward-char)))))
+ (insert coding-system)))
+ ((looking-at "\\s *#.*coding\\s *[:=]"))
+ (t (when ruby-insert-encoding-magic-comment
+ (insert "# -*- coding: " coding-system " -*-\n"))))))))
+
+ (define-key ruby-mode-map "\C-cU" 'ruby-encode-decode-unicode)
+
+ (defun ruby-encode-unicode (beg end)
+ "Convert non-ascii string in the given region to \\u{} form."
+ (interactive "r")
+ (setq end (set-marker (make-marker) end))
+ (goto-char beg)
+ (while (and (< (point) end)
+ (re-search-forward "\\([\C-@-\C-I\C-K\C-_\C-?]+\\)\\|[^\C-@-\C-?]+" end t))
+ (let ((str (match-string-no-properties 0)) sep b e f)
+ (if (match-beginning 1)
+ (setq b "" e "" sep ""
+ f (lambda (c)
+ (cond ((= c ?\t) "\\t")
+ ((= c ?\r) "\\r")
+ ((= c ?\e) "\\e")
+ ((= c ?\f) "\\f")
+ ((= c ?\b) "\\b")
+ ((= c ?\v) "\\v")
+ ((= c ?\C-?) "\\c?")
+ ((concat "\\c" (char-to-string (logior c #x40)))))))
+ (setq b "\\u{" e "}" sep " " f (lambda (c) (format "%x" c))))
+ (setq str (mapconcat f str sep))
+ (delete-region (match-beginning 0) (match-end 0))
+ (insert b str e))))
+
+ (defun ruby-decode-unicode (beg end)
+ "Convert escaped Unicode in the given region to raw string."
+ (interactive "r")
+ (setq end (set-marker (make-marker) end))
+ (goto-char beg)
+ (while (and (< (point) end)
+ (re-search-forward "\\\\u\\([0-9a-fA-F]\\{4\\}\\)\\|\\\\u{\\([0-9a-fA-F \t]+\\)}" end t))
+ (let ((b (match-beginning 0)) (e (match-end 0))
+ (s (match-string-no-properties 1)))
+ (if s
+ (setq s (cons s nil))
+ (goto-char (match-beginning 2))
+ (while (looking-at "[ \t]*\\([0-9a-fA-F]+\\)")
+ (setq s (cons (match-string-no-properties 1) s))
+ (goto-char (match-end 0))))
+ (setq s (mapconcat (lambda (c) (format "%c" (string-to-int c 16)))
+ (nreverse s) ""))
+ (delete-region b e)
+ (insert s))
+ ))
+
+ (defun ruby-encode-decode-unicode (dec beg end)
+ "Convert Unicode <-> \\u{} in the given region."
+ (interactive "P\nr")
+ (if dec (ruby-decode-unicode beg end) (ruby-encode-unicode beg end)))
+
+ (defun ruby-insert-heredoc-code-block (arg)
+ "Insert indented here document code block"
+ (interactive "P")
+ (let ((c (if arg "~" "-")))
+ (insert "\"#{<<" c "\"{#\"}\\n#{<<" c "'};'}\""))
+ (end-of-line)
+ (if (eobp) (insert "\n") (forward-char))
+ (indent-region (point)
+ (progn (insert "{#\n" "};\n") (point)))
+ (beginning-of-line 0))
+ (define-key ruby-mode-map "\C-cH" 'ruby-insert-heredoc-code-block)
+ ))
+
+;; monkey-patching ruby-mode.el in Emacs 24, as r49872.
+(when (and (boundp 'ruby-syntax-before-regexp-re)
+ (not (string-match ruby-syntax-before-regexp-re "foo {|" 1)))
+ (replace-regexp-in-string "\\[\\[" "\\&{|" ruby-syntax-before-regexp-re))
+
+(provide 'ruby-additional)
+
+;;; ruby-additional.el ends here
diff --git a/misc/ruby-electric.el b/misc/ruby-electric.el
new file mode 100644
index 0000000000..61e84d2adb
--- /dev/null
+++ b/misc/ruby-electric.el
@@ -0,0 +1,583 @@
+;;; ruby-electric.el --- Minor mode for electrically editing ruby code
+;;
+;; Authors: Dee Zsombor <dee dot zsombor at gmail dot com>
+;; Yukihiro Matsumoto
+;; Nobuyoshi Nakada
+;; Akinori MUSHA <knu@iDaemons.org>
+;; Jakub Kuźma <qoobaa@gmail.com>
+;; Maintainer: Akinori MUSHA <knu@iDaemons.org>
+;; Created: 6 Mar 2005
+;; URL: https://github.com/knu/ruby-electric.el
+;; Keywords: languages ruby
+;; License: The same license terms as Ruby
+;; Version: 2.3.1
+
+;;; Commentary:
+;;
+;; `ruby-electric-mode' accelerates code writing in ruby by making
+;; some keys "electric" and automatically supplying with closing
+;; parentheses and "end" as appropriate.
+;;
+;; This work was originally inspired by a code snippet posted by
+;; [Frederick Ros](https://github.com/sleeper).
+;;
+;; Add the following line to enable ruby-electric-mode under
+;; ruby-mode.
+;;
+;; (eval-after-load "ruby-mode"
+;; '(add-hook 'ruby-mode-hook 'ruby-electric-mode))
+;;
+;; Type M-x customize-group ruby-electric for configuration.
+
+;;; Code:
+
+(require 'ruby-mode)
+
+(eval-when-compile
+ (require 'cl))
+
+(defgroup ruby-electric nil
+ "Minor mode providing electric editing commands for ruby files"
+ :group 'ruby)
+
+(defconst ruby-electric-expandable-bar-re
+ "\\s-\\(do\\|{\\)\\s-*|")
+
+(defconst ruby-electric-delimiters-alist
+ '((?\{ :name "Curly brace" :handler ruby-electric-curlies :closing ?\})
+ (?\[ :name "Square brace" :handler ruby-electric-matching-char :closing ?\])
+ (?\( :name "Round brace" :handler ruby-electric-matching-char :closing ?\))
+ (?\' :name "Quote" :handler ruby-electric-matching-char)
+ (?\" :name "Double quote" :handler ruby-electric-matching-char)
+ (?\` :name "Back quote" :handler ruby-electric-matching-char)
+ (?\| :name "Vertical bar" :handler ruby-electric-bar)
+ (?\# :name "Hash" :handler ruby-electric-hash)))
+
+(defvar ruby-electric-matching-delimeter-alist
+ (apply 'nconc
+ (mapcar #'(lambda (x)
+ (let ((delim (car x))
+ (plist (cdr x)))
+ (if (eq (plist-get plist :handler) 'ruby-electric-matching-char)
+ (list (cons delim (or (plist-get plist :closing)
+ delim))))))
+ ruby-electric-delimiters-alist)))
+
+(defvar ruby-electric-expandable-keyword-re)
+
+(defmacro ruby-electric--try-insert-and-do (string &rest body)
+ (declare (indent 1))
+ `(let ((before (point))
+ (after (progn
+ (insert ,string)
+ (point))))
+ (unwind-protect
+ (progn ,@body)
+ (delete-region before after)
+ (goto-char before))))
+
+(defconst ruby-modifier-beg-symbol-re
+ (regexp-opt ruby-modifier-beg-keywords 'symbols))
+
+(defun ruby-electric--modifier-keyword-at-point-p ()
+ "Test if there is a modifier keyword at point."
+ (and (looking-at ruby-modifier-beg-symbol-re)
+ (let ((end (match-end 1)))
+ (not (looking-back "\\."))
+ (save-excursion
+ (let ((indent1 (ruby-electric--try-insert-and-do "\n"
+ (ruby-calculate-indent)))
+ (indent2 (save-excursion
+ (goto-char end)
+ (ruby-electric--try-insert-and-do " x\n"
+ (ruby-calculate-indent)))))
+ (= indent1 indent2))))))
+
+(defconst ruby-block-mid-symbol-re
+ (regexp-opt ruby-block-mid-keywords 'symbols))
+
+(defun ruby-electric--block-mid-keyword-at-point-p ()
+ "Test if there is a block mid keyword at point."
+ (and (looking-at ruby-block-mid-symbol-re)
+ (looking-back "^\\s-*")))
+
+(defconst ruby-block-beg-symbol-re
+ (regexp-opt ruby-block-beg-keywords 'symbols))
+
+(defun ruby-electric--block-beg-keyword-at-point-p ()
+ "Test if there is a block beginning keyword at point."
+ (and (looking-at ruby-block-beg-symbol-re)
+ (if (string= (match-string 1) "do")
+ (looking-back "\\s-")
+ (not (looking-back "\\.")))
+ ;; (not (ruby-electric--modifier-keyword-at-point-p)) ;; implicit assumption
+ ))
+
+(defcustom ruby-electric-keywords-alist
+ '(("begin" . end)
+ ("case" . end)
+ ("class" . end)
+ ("def" . end)
+ ("do" . end)
+ ("else" . reindent)
+ ("elsif" . reindent)
+ ("end" . reindent)
+ ("ensure" . reindent)
+ ("for" . end)
+ ("if" . end)
+ ("module" . end)
+ ("rescue" . reindent)
+ ("unless" . end)
+ ("until" . end)
+ ("when" . reindent)
+ ("while" . end))
+ "Alist of keywords and actions to define how to react to space
+or return right after each keyword. In each (KEYWORD . ACTION)
+cons, ACTION can be set to one of the following values:
+
+ `reindent' Reindent the line.
+
+ `end' Reindent the line and auto-close the keyword with
+ end if applicable.
+
+ `nil' Do nothing.
+"
+ :type '(repeat (cons (string :tag "Keyword")
+ (choice :tag "Action"
+ :menu-tag "Action"
+ (const :tag "Auto-close with end"
+ :value end)
+ (const :tag "Auto-reindent"
+ :value reindent)
+ (const :tag "None"
+ :value nil))))
+ :set (lambda (sym val)
+ (set sym val)
+ (let (keywords)
+ (dolist (x val)
+ (let ((keyword (car x))
+ (action (cdr x)))
+ (if action
+ (setq keywords (cons keyword keywords)))))
+ (setq ruby-electric-expandable-keyword-re
+ (concat (regexp-opt keywords 'symbols)
+ "$"))))
+ :group 'ruby-electric)
+
+(defvar ruby-electric-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map " " 'ruby-electric-space/return)
+ (define-key map [remap delete-backward-char] 'ruby-electric-delete-backward-char)
+ (define-key map [remap newline] 'ruby-electric-space/return)
+ (define-key map [remap newline-and-indent] 'ruby-electric-space/return)
+ (define-key map [remap electric-newline-and-maybe-indent] 'ruby-electric-space/return)
+ (define-key map [remap reindent-then-newline-and-indent] 'ruby-electric-space/return)
+ (dolist (x ruby-electric-delimiters-alist)
+ (let* ((delim (car x))
+ (plist (cdr x))
+ (name (plist-get plist :name))
+ (func (plist-get plist :handler))
+ (closing (plist-get plist :closing)))
+ (define-key map (char-to-string delim) func)
+ (if closing
+ (define-key map (char-to-string closing) 'ruby-electric-closing-char))))
+ map)
+ "Keymap used in ruby-electric-mode")
+
+(defcustom ruby-electric-expand-delimiters-list '(all)
+ "*List of contexts where matching delimiter should be inserted.
+The word 'all' will do all insertions."
+ :type `(set :extra-offset 8
+ (const :tag "Everything" all)
+ ,@(apply 'list
+ (mapcar #'(lambda (x)
+ `(const :tag ,(plist-get (cdr x) :name)
+ ,(car x)))
+ ruby-electric-delimiters-alist)))
+ :group 'ruby-electric)
+
+(defcustom ruby-electric-newline-before-closing-bracket nil
+ "*Non-nil means a newline should be inserted before an
+automatically inserted closing bracket."
+ :type 'boolean :group 'ruby-electric)
+
+(defcustom ruby-electric-autoindent-on-closing-char nil
+ "*Non-nil means the current line should be automatically
+indented when a closing character is manually typed in."
+ :type 'boolean :group 'ruby-electric)
+
+(defvar ruby-electric-mode-hook nil
+ "Called after `ruby-electric-mode' is turned on.")
+
+;;;###autoload
+(define-minor-mode ruby-electric-mode
+ "Toggle Ruby Electric minor mode.
+With no argument, this command toggles the mode. Non-null prefix
+argument turns on the mode. Null prefix argument turns off the
+mode.
+
+When Ruby Electric mode is enabled, an indented 'end' is
+heuristicaly inserted whenever typing a word like 'module',
+'class', 'def', 'if', 'unless', 'case', 'until', 'for', 'begin',
+'do' followed by a space. Single, double and back quotes as well
+as braces are paired auto-magically. Expansion does not occur
+inside comments and strings. Note that you must have Font Lock
+enabled."
+ ;; initial value.
+ nil
+ ;;indicator for the mode line.
+ " REl"
+ ;;keymap
+ ruby-electric-mode-map
+ (if ruby-electric-mode
+ (run-hooks 'ruby-electric-mode-hook)))
+
+(defun ruby-electric-space/return-fallback ()
+ (if (or (eq this-original-command 'ruby-electric-space/return)
+ (null (ignore-errors
+ ;; ac-complete may fail if there is nothing left to complete
+ (call-interactively this-original-command)
+ (setq this-command this-original-command))))
+ ;; fall back to a globally bound command
+ (let ((command (global-key-binding (char-to-string last-command-event) t)))
+ (and command
+ (call-interactively (setq this-command command))))))
+
+(defun ruby-electric-space/return (arg)
+ (interactive "*P")
+ (and (boundp 'sp-last-operation)
+ (setq sp-delayed-pair nil))
+ (cond ((or arg
+ (region-active-p))
+ (or (= last-command-event ?\s)
+ (setq last-command-event ?\n))
+ (ruby-electric-replace-region-or-insert))
+ ((ruby-electric-space/return-can-be-expanded-p)
+ (let (action)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (let* ((keyword (match-string 1))
+ (allowed-actions
+ (cond ((ruby-electric--modifier-keyword-at-point-p)
+ '(reindent)) ;; no end necessary
+ ((ruby-electric--block-mid-keyword-at-point-p)
+ '(reindent)) ;; ditto
+ ((ruby-electric--block-beg-keyword-at-point-p)
+ '(end reindent)))))
+ (if allowed-actions
+ (setq action
+ (let ((action (cdr (assoc keyword ruby-electric-keywords-alist))))
+ (and (memq action allowed-actions)
+ action))))))
+ (cond ((eq action 'end)
+ (ruby-indent-line)
+ (save-excursion
+ (newline)
+ (ruby-electric-end)))
+ ((eq action 'reindent)
+ (ruby-indent-line)))
+ (ruby-electric-space/return-fallback)))
+ ((and (eq this-original-command 'newline-and-indent)
+ (ruby-electric-comment-at-point-p))
+ (call-interactively (setq this-command 'comment-indent-new-line)))
+ (t
+ (ruby-electric-space/return-fallback))))
+
+(defun ruby-electric--get-faces-at-point ()
+ (let* ((point (point))
+ (value (or
+ (get-text-property point 'read-face-name)
+ (get-text-property point 'face))))
+ (if (listp value) value (list value))))
+
+(defun ruby-electric--faces-include-p (pfaces &rest faces)
+ (and ruby-electric-mode
+ (loop for face in faces
+ thereis (memq face pfaces))))
+
+(defun ruby-electric--faces-at-point-include-p (&rest faces)
+ (apply 'ruby-electric--faces-include-p
+ (ruby-electric--get-faces-at-point)
+ faces))
+
+(defun ruby-electric-code-face-p (faces)
+ (not (ruby-electric--faces-include-p
+ faces
+ 'font-lock-string-face
+ 'font-lock-comment-face
+ 'enh-ruby-string-delimiter-face
+ 'enh-ruby-heredoc-delimiter-face
+ 'enh-ruby-regexp-delimiter-face
+ 'enh-ruby-regexp-face)))
+
+(defun ruby-electric-code-at-point-p ()
+ (ruby-electric-code-face-p
+ (ruby-electric--get-faces-at-point)))
+
+(defun ruby-electric-string-face-p (faces)
+ (ruby-electric--faces-include-p
+ faces
+ 'font-lock-string-face
+ 'enh-ruby-string-delimiter-face
+ 'enh-ruby-heredoc-delimiter-face
+ 'enh-ruby-regexp-delimiter-face
+ 'enh-ruby-regexp-face))
+
+(defun ruby-electric-string-at-point-p ()
+ (ruby-electric-string-face-p
+ (ruby-electric--get-faces-at-point)))
+
+(defun ruby-electric-comment-at-point-p ()
+ (ruby-electric--faces-at-point-include-p
+ 'font-lock-comment-face))
+
+(defun ruby-electric-escaped-p()
+ (let ((f nil))
+ (save-excursion
+ (while (char-equal ?\\ (preceding-char))
+ (backward-char 1)
+ (setq f (not f))))
+ f))
+
+(defun ruby-electric-command-char-expandable-punct-p(char)
+ (or (memq 'all ruby-electric-expand-delimiters-list)
+ (memq char ruby-electric-expand-delimiters-list)))
+
+(defun ruby-electric-space/return-can-be-expanded-p()
+ (and (ruby-electric-code-at-point-p)
+ (looking-back ruby-electric-expandable-keyword-re)))
+
+(defun ruby-electric-replace-region-or-insert ()
+ (and (region-active-p)
+ (bound-and-true-p delete-selection-mode)
+ (fboundp 'delete-selection-helper)
+ (delete-selection-helper (get 'self-insert-command 'delete-selection)))
+ (insert (make-string (prefix-numeric-value current-prefix-arg)
+ last-command-event))
+ (setq this-command 'self-insert-command))
+
+(defmacro ruby-electric-insert (arg &rest body)
+ `(cond ((and
+ (null ,arg)
+ (ruby-electric-command-char-expandable-punct-p last-command-event))
+ (let ((region-beginning
+ (cond ((region-active-p)
+ (prog1
+ (save-excursion
+ (goto-char (region-beginning))
+ (insert last-command-event)
+ (point))
+ (goto-char (region-end))))
+ (t
+ (insert last-command-event)
+ nil)))
+ (faces-at-point
+ (ruby-electric--get-faces-at-point)))
+ ,@body
+ (and region-beginning
+ ;; If no extra character is inserted, go back to the
+ ;; region beginning.
+ (eq this-command 'self-insert-command)
+ (goto-char region-beginning))))
+ ((ruby-electric-replace-region-or-insert))))
+
+(defun ruby-electric-curlies (arg)
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (cond
+ ((or (ruby-electric-code-at-point-p)
+ (ruby-electric--faces-include-p
+ faces-at-point
+ 'enh-ruby-string-delimiter-face
+ 'enh-ruby-regexp-delimiter-face))
+ (save-excursion
+ (insert "}")
+ (font-lock-fontify-region (line-beginning-position) (point)))
+ (cond
+ ((or (ruby-electric-string-at-point-p) ;; %w{}, %r{}, etc.
+ (looking-back "%[QqWwRrxIis]{"))
+ (if region-beginning
+ (forward-char 1)))
+ (ruby-electric-newline-before-closing-bracket
+ (cond (region-beginning
+ (save-excursion
+ (goto-char region-beginning)
+ (newline))
+ (newline)
+ (forward-char 1)
+ (indent-region region-beginning (line-end-position)))
+ (t
+ (insert " ")
+ (save-excursion
+ (newline)
+ (ruby-indent-line t)))))
+ (t
+ (if region-beginning
+ (save-excursion
+ (goto-char region-beginning)
+ (insert " "))
+ (insert " "))
+ (insert " ")
+ (backward-char 1)
+ (and region-beginning
+ (forward-char 1)))))
+ ((ruby-electric-string-at-point-p)
+ (let ((start-position (1- (or region-beginning (point)))))
+ (cond
+ ((char-equal ?\# (char-before start-position))
+ (unless (save-excursion
+ (goto-char (1- start-position))
+ (ruby-electric-escaped-p))
+ (insert "}")
+ (or region-beginning
+ (backward-char 1))))
+ ((or
+ (ruby-electric-command-char-expandable-punct-p ?\#)
+ (save-excursion
+ (goto-char start-position)
+ (ruby-electric-escaped-p)))
+ (if region-beginning
+ (goto-char region-beginning))
+ (setq this-command 'self-insert-command))
+ (t
+ (save-excursion
+ (goto-char start-position)
+ (insert "#"))
+ (insert "}")
+ (or region-beginning
+ (backward-char 1))))))
+ (t
+ (delete-char -1)
+ (ruby-electric-replace-region-or-insert)))))
+
+(defun ruby-electric-hash (arg)
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (if (ruby-electric-string-at-point-p)
+ (let ((start-position (1- (or region-beginning (point)))))
+ (cond
+ ((char-equal (following-char) ?')) ;; likely to be in ''
+ ((save-excursion
+ (goto-char start-position)
+ (ruby-electric-escaped-p)))
+ (region-beginning
+ (save-excursion
+ (goto-char (1+ start-position))
+ (insert "{"))
+ (insert "}"))
+ (t
+ (insert "{")
+ (save-excursion
+ (insert "}")))))
+ (delete-char -1)
+ (ruby-electric-replace-region-or-insert))))
+
+(defun ruby-electric-matching-char (arg)
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (let ((closing (cdr (assoc last-command-event
+ ruby-electric-matching-delimeter-alist))))
+ (cond
+ ;; quotes
+ ((char-equal closing last-command-event)
+ (cond ((not (ruby-electric-string-face-p faces-at-point))
+ (if region-beginning
+ ;; escape quotes of the same kind, backslash and hash
+ (let ((re (format "[%c\\%s]"
+ last-command-event
+ (if (char-equal last-command-event ?\")
+ "#" "")))
+ (bound (point)))
+ (save-excursion
+ (goto-char region-beginning)
+ (while (re-search-forward re bound t)
+ (let ((end (point)))
+ (replace-match "\\\\\\&")
+ (setq bound (+ bound (- (point) end))))))))
+ (insert closing)
+ (or region-beginning
+ (backward-char 1)))
+ (t
+ (and (eq last-command 'ruby-electric-matching-char)
+ (char-equal (following-char) closing) ;; repeated quotes
+ (delete-char 1))
+ (setq this-command 'self-insert-command))))
+ ((ruby-electric-code-at-point-p)
+ (insert closing)
+ (or region-beginning
+ (backward-char 1)))))))
+
+(defun ruby-electric-closing-char(arg)
+ (interactive "*P")
+ (cond
+ (arg
+ (ruby-electric-replace-region-or-insert))
+ ((and
+ (eq last-command 'ruby-electric-curlies)
+ (= last-command-event ?})
+ (not (char-equal (preceding-char) last-command-event))) ;; {}
+ (if (char-equal (following-char) ?\n) (delete-char 1))
+ (delete-horizontal-space)
+ (forward-char))
+ ((and
+ (= last-command-event (following-char))
+ (not (char-equal (preceding-char) last-command-event))
+ (memq last-command '(ruby-electric-matching-char
+ ruby-electric-closing-char))) ;; ()/[] and (())/[[]]
+ (forward-char))
+ (t
+ (ruby-electric-replace-region-or-insert)
+ (if ruby-electric-autoindent-on-closing-char
+ (ruby-indent-line)))))
+
+(defun ruby-electric-bar(arg)
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (cond ((and (ruby-electric-code-at-point-p)
+ (looking-back ruby-electric-expandable-bar-re))
+ (save-excursion (insert "|")))
+ (t
+ (delete-char -1)
+ (ruby-electric-replace-region-or-insert)))))
+
+(defun ruby-electric-delete-backward-char(arg)
+ (interactive "*p")
+ (cond ((memq last-command '(ruby-electric-matching-char
+ ruby-electric-bar))
+ (delete-char 1))
+ ((eq last-command 'ruby-electric-curlies)
+ (cond ((eolp)
+ (cond ((char-equal (preceding-char) ?\s)
+ (setq this-command last-command))
+ ((char-equal (preceding-char) ?{)
+ (and (looking-at "[ \t\n]*}")
+ (delete-char (- (match-end 0) (match-beginning 0)))))))
+ ((char-equal (following-char) ?\s)
+ (setq this-command last-command)
+ (delete-char 1))
+ ((char-equal (following-char) ?})
+ (delete-char 1))))
+ ((eq last-command 'ruby-electric-hash)
+ (and (char-equal (preceding-char) ?{)
+ (delete-char 1))))
+ (delete-char (- arg)))
+
+(put 'ruby-electric-delete-backward-char 'delete-selection 'supersede)
+
+(defun ruby-electric-end ()
+ (interactive)
+ (if (eq (char-syntax (preceding-char)) ?w)
+ (insert " "))
+ (insert "end")
+ (save-excursion
+ (if (eq (char-syntax (following-char)) ?w)
+ (insert " "))
+ (ruby-indent-line t)))
+
+(provide 'ruby-electric)
+
+;;; ruby-electric.el ends here
diff --git a/misc/ruby-mode.el b/misc/ruby-mode.el
new file mode 100644
index 0000000000..b1abd18a9e
--- /dev/null
+++ b/misc/ruby-mode.el
@@ -0,0 +1,1584 @@
+;;; ruby-mode.el --- Major mode for editing Ruby files
+
+;; Copyright (C) 1994, 1995, 1996 1997, 1998, 1999, 2000, 2001,
+;; 2002,2003, 2004, 2005, 2006, 2007, 2008
+;; Free Software Foundation, Inc.
+
+;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada
+;; URL: http://www.emacswiki.org/cgi-bin/wiki/RubyMode
+;; Created: Fri Feb 4 14:49:13 JST 1994
+;; Keywords: languages ruby
+;; Version: 0.9
+
+;; This file is not part of GNU Emacs. However, a newer version of
+;; ruby-mode is included in recent releases of GNU Emacs (version 23
+;; and up), but the new version is not guaranteed to be compatible
+;; with older versions of Emacs or XEmacs. This file is the last
+;; version that aims to keep this compatibility.
+
+;; You can also get the latest version from the Emacs Lisp Package
+;; Archive: http://tromey.com/elpa
+
+;; This file is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; It is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+;; License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with it. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Provides font-locking, indentation support, and navigation for Ruby code.
+;;
+;; If you're installing manually, you should add this to your .emacs
+;; file after putting it on your load path:
+;;
+;; (autoload 'ruby-mode "ruby-mode" "Major mode for ruby files" t)
+;; (add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
+;; (add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode))
+;;
+
+;;; Code:
+
+(defconst ruby-mode-revision "$Revision$"
+ "Ruby mode revision string.")
+
+(defconst ruby-mode-version
+ (and (string-match "[0-9.]+" ruby-mode-revision)
+ (substring ruby-mode-revision (match-beginning 0) (match-end 0)))
+ "Ruby mode version number.")
+
+(defconst ruby-keyword-end-re
+ (if (string-match "\\_>" "ruby")
+ "\\_>"
+ "\\>"))
+
+(defconst ruby-block-beg-keywords
+ '("class" "module" "def" "if" "unless" "case" "while" "until" "for" "begin" "do")
+ "Keywords at the beginning of blocks.")
+
+(defconst ruby-block-beg-re
+ (regexp-opt ruby-block-beg-keywords)
+ "Regexp to match the beginning of blocks.")
+
+(defconst ruby-non-block-do-re
+ (concat (regexp-opt '("while" "until" "for" "rescue") t) ruby-keyword-end-re)
+ "Regexp to match")
+
+(defconst ruby-indent-beg-re
+ (concat "\\(\\s *" (regexp-opt '("class" "module" "def") t) "\\)\\|"
+ (regexp-opt '("if" "unless" "case" "while" "until" "for" "begin")))
+ "Regexp to match where the indentation gets deeper.")
+
+(defconst ruby-modifier-beg-keywords
+ '("if" "unless" "while" "until")
+ "Modifiers that are the same as the beginning of blocks.")
+
+(defconst ruby-modifier-beg-re
+ (regexp-opt ruby-modifier-beg-keywords)
+ "Regexp to match modifiers same as the beginning of blocks.")
+
+(defconst ruby-modifier-re
+ (regexp-opt (cons "rescue" ruby-modifier-beg-keywords))
+ "Regexp to match modifiers.")
+
+(defconst ruby-block-mid-keywords
+ '("then" "else" "elsif" "when" "rescue" "ensure")
+ "Keywords where the indentation gets shallower in middle of block statements.")
+
+(defconst ruby-block-mid-re
+ (regexp-opt ruby-block-mid-keywords)
+ "Regexp to match where the indentation gets shallower in middle of block statements.")
+
+(defconst ruby-block-op-keywords
+ '("and" "or" "not")
+ "Block operators.")
+
+(defconst ruby-block-hanging-re
+ (regexp-opt (append ruby-modifier-beg-keywords ruby-block-op-keywords))
+ "Regexp to match hanging block modifiers.")
+
+(defconst ruby-block-end-re "\\_<end\\_>")
+
+(defconst ruby-here-doc-beg-re
+ "\\(<\\)<\\([-~]\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)")
+
+(defconst ruby-here-doc-end-re
+ "^\\([ \t]+\\)?\\(.*\\)\\(.\\)$")
+
+(defun ruby-here-doc-end-match ()
+ (concat "^"
+ (if (match-string 2) "[ \t]*" nil)
+ (regexp-quote
+ (or (match-string 4)
+ (match-string 5)
+ (match-string 6)))))
+
+(defun ruby-here-doc-beg-match ()
+ (let ((contents (concat
+ (regexp-quote (concat (match-string 2) (match-string 3)))
+ (if (string= (match-string 3) "_") "\\B" "\\b"))))
+ (concat "<<"
+ (let ((match (match-string 1)))
+ (if (and match (> (length match) 0))
+ (concat "\\(?:[-~]\\([\"']?\\)\\|\\([\"']\\)" (match-string 1) "\\)"
+ contents "\\(\\1\\|\\2\\)")
+ (concat "[-~]?\\([\"']\\|\\)" contents "\\1"))))))
+
+(defconst ruby-delimiter
+ (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\_<\\("
+ ruby-block-beg-re
+ "\\)\\_>\\|" ruby-block-end-re
+ "\\|^=begin\\|" ruby-here-doc-beg-re)
+ )
+
+(defconst ruby-negative
+ (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|"
+ ruby-block-end-re "\\|}\\|\\]\\)")
+ "Regexp to match where the indentation gets shallower.")
+
+(defconst ruby-operator-chars "-,.+*/%&|^~=<>:")
+(defconst ruby-operator-re (concat "[" ruby-operator-chars "]"))
+
+(defconst ruby-symbol-chars "a-zA-Z0-9_")
+(defconst ruby-symbol-re (concat "[" ruby-symbol-chars "]"))
+
+(defvar ruby-mode-abbrev-table nil
+ "Abbrev table in use in ruby-mode buffers.")
+
+(define-abbrev-table 'ruby-mode-abbrev-table ())
+
+(defvar ruby-mode-map nil "Keymap used in ruby mode.")
+
+(if ruby-mode-map
+ nil
+ (setq ruby-mode-map (make-sparse-keymap))
+ (define-key ruby-mode-map "{" 'ruby-electric-brace)
+ (define-key ruby-mode-map "}" 'ruby-electric-brace)
+ (define-key ruby-mode-map "\e\C-a" 'ruby-beginning-of-defun)
+ (define-key ruby-mode-map "\e\C-e" 'ruby-end-of-defun)
+ (define-key ruby-mode-map "\e\C-b" 'ruby-backward-sexp)
+ (define-key ruby-mode-map "\e\C-f" 'ruby-forward-sexp)
+ (define-key ruby-mode-map "\e\C-p" 'ruby-beginning-of-block)
+ (define-key ruby-mode-map "\e\C-n" 'ruby-end-of-block)
+ (define-key ruby-mode-map "\e\C-h" 'ruby-mark-defun)
+ (define-key ruby-mode-map "\e\C-q" 'ruby-indent-exp)
+ (define-key ruby-mode-map "\t" 'ruby-indent-command)
+ (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end)
+ (define-key ruby-mode-map "\C-j" 'ruby-reindent-then-newline-and-indent)
+ (define-key ruby-mode-map "\C-c{" 'ruby-toggle-block)
+ (define-key ruby-mode-map "\C-c\C-u" 'uncomment-region))
+
+(defvar ruby-mode-syntax-table nil
+ "Syntax table in use in ruby-mode buffers.")
+
+(if ruby-mode-syntax-table
+ ()
+ (setq ruby-mode-syntax-table (make-syntax-table))
+ (modify-syntax-entry ?\' "\"" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\" "\"" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\` "\"" ruby-mode-syntax-table)
+ (modify-syntax-entry ?# "<" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\n ">" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\\ "\\" ruby-mode-syntax-table)
+ (modify-syntax-entry ?$ "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?? "_" ruby-mode-syntax-table)
+ (modify-syntax-entry ?_ "_" ruby-mode-syntax-table)
+ (modify-syntax-entry ?: "_" ruby-mode-syntax-table)
+ (modify-syntax-entry ?< "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?> "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?& "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?| "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?% "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?= "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?/ "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?+ "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?* "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?- "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?\; "." ruby-mode-syntax-table)
+ (modify-syntax-entry ?\( "()" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\) ")(" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\{ "(}" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\} "){" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\[ "(]" ruby-mode-syntax-table)
+ (modify-syntax-entry ?\] ")[" ruby-mode-syntax-table)
+ )
+
+(defcustom ruby-indent-tabs-mode nil
+ "*Indentation can insert tabs in ruby mode if this is non-nil."
+ :type 'boolean :group 'ruby)
+(put 'ruby-indent-tabs-mode 'safe-local-variable 'booleanp)
+
+(defcustom ruby-indent-level 2
+ "*Indentation of ruby statements."
+ :type 'integer :group 'ruby)
+(put 'ruby-indent-level 'safe-local-variable 'integerp)
+
+(defcustom ruby-comment-column 32
+ "*Indentation column of comments."
+ :type 'integer :group 'ruby)
+(put 'ruby-comment-column 'safe-local-variable 'integerp)
+
+(defcustom ruby-deep-arglist t
+ "*Deep indent lists in parenthesis when non-nil.
+Also ignores spaces after parenthesis when 'space."
+ :group 'ruby)
+(put 'ruby-deep-arglist 'safe-local-variable 'booleanp)
+
+(defcustom ruby-deep-indent-paren '(?\( ?\[ ?\] t)
+ "*Deep indent lists in parenthesis when non-nil. t means continuous line.
+Also ignores spaces after parenthesis when 'space."
+ :group 'ruby)
+
+(defcustom ruby-deep-indent-paren-style 'space
+ "Default deep indent style."
+ :options '(t nil space) :group 'ruby)
+
+(defcustom ruby-encoding-map
+ '((us-ascii . nil) ;; Do not put coding: us-ascii
+ (utf-8 . nil) ;; Do not put coding: utf-8
+ (shift-jis . cp932) ;; Emacs charset name of Shift_JIS
+ (shift_jis . cp932) ;; MIME charset name of Shift_JIS
+ (japanese-cp932 . cp932)) ;; Emacs charset name of CP932
+ "Alist to map encoding name from Emacs to Ruby.
+Associating an encoding name with nil means it needs not be
+explicitly declared in magic comment."
+ :type '(repeat (cons (symbol :tag "From") (symbol :tag "To")))
+ :group 'ruby)
+
+(defcustom ruby-use-encoding-map t
+ "*Use `ruby-encoding-map' to set encoding magic comment if this is non-nil."
+ :type 'boolean :group 'ruby)
+
+(defvar ruby-indent-point nil "internal variable")
+
+(eval-when-compile (require 'cl))
+(defun ruby-imenu-create-index-in-block (prefix beg end)
+ (let ((index-alist '()) (case-fold-search nil)
+ name next pos decl sing)
+ (goto-char beg)
+ (while (re-search-forward "^\\s *\\(\\(class\\s +\\|\\(class\\s *<<\\s *\\)\\|module\\s +\\)\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\s +\\([^\(\n ]+\\)\\)" end t)
+ (setq sing (match-beginning 3))
+ (setq decl (match-string 5))
+ (setq next (match-end 0))
+ (setq name (or (match-string 4) (match-string 6)))
+ (setq pos (match-beginning 0))
+ (cond
+ ((string= "alias" decl)
+ (if prefix (setq name (concat prefix name)))
+ (push (cons name pos) index-alist))
+ ((string= "def" decl)
+ (if prefix
+ (setq name
+ (cond
+ ((string-match "^self\." name)
+ (concat (substring prefix 0 -1) (substring name 4)))
+ (t (concat prefix name)))))
+ (push (cons name pos) index-alist)
+ (ruby-accurate-end-of-block end))
+ (t
+ (if (string= "self" name)
+ (if prefix (setq name (substring prefix 0 -1)))
+ (if prefix (setq name (concat (substring prefix 0 -1) "::" name)))
+ (push (cons name pos) index-alist))
+ (ruby-accurate-end-of-block end)
+ (setq beg (point))
+ (setq index-alist
+ (nconc (ruby-imenu-create-index-in-block
+ (concat name (if sing "." "#"))
+ next beg) index-alist))
+ (goto-char beg))))
+ index-alist))
+
+(defun ruby-imenu-create-index ()
+ (nreverse (ruby-imenu-create-index-in-block nil (point-min) nil)))
+
+(defun ruby-accurate-end-of-block (&optional end)
+ (let (state)
+ (or end (setq end (point-max)))
+ (while (and (setq state (apply 'ruby-parse-partial end state))
+ (>= (nth 2 state) 0) (< (point) end)))))
+
+(defun ruby-mode-variables ()
+ (set-syntax-table ruby-mode-syntax-table)
+ (setq show-trailing-whitespace t)
+ (setq local-abbrev-table ruby-mode-abbrev-table)
+ (make-local-variable 'indent-line-function)
+ (setq indent-line-function 'ruby-indent-line)
+ (make-local-variable 'require-final-newline)
+ (setq require-final-newline t)
+ (make-local-variable 'comment-start)
+ (setq comment-start "# ")
+ (make-local-variable 'comment-end)
+ (setq comment-end "")
+ (make-local-variable 'comment-column)
+ (setq comment-column ruby-comment-column)
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip "#+ *")
+ (setq indent-tabs-mode ruby-indent-tabs-mode)
+ (make-local-variable 'parse-sexp-ignore-comments)
+ (setq parse-sexp-ignore-comments t)
+ (make-local-variable 'parse-sexp-lookup-properties)
+ (setq parse-sexp-lookup-properties t)
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start (concat "$\\|" page-delimiter))
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate paragraph-start)
+ (make-local-variable 'paragraph-ignore-fill-prefix)
+ (setq paragraph-ignore-fill-prefix t))
+
+(defun ruby-mode-set-encoding ()
+ "Insert or update a magic comment header with the proper encoding.
+`ruby-encoding-map' is looked up to convert an encoding name from
+Emacs to Ruby."
+ (let* ((nonascii
+ (save-excursion
+ (widen)
+ (goto-char (point-min))
+ (re-search-forward "[^\0-\177]" nil t)))
+ (coding-system
+ (or coding-system-for-write
+ buffer-file-coding-system))
+ (coding-system
+ (and coding-system
+ (coding-system-change-eol-conversion coding-system nil)))
+ (coding-system
+ (and coding-system
+ (or
+ (coding-system-get coding-system :mime-charset)
+ (let ((coding-type (coding-system-get coding-system :coding-type)))
+ (cond ((eq coding-type 'undecided)
+ (if nonascii
+ (or (and (coding-system-get coding-system :prefer-utf-8)
+ 'utf-8)
+ (coding-system-get default-buffer-file-coding-system :coding-type)
+ 'ascii-8bit)))
+ ((memq coding-type '(utf-8 shift-jis))
+ coding-type)
+ (t coding-system))))))
+ (coding-system
+ (or coding-system
+ 'us-ascii))
+ (coding-system
+ (let ((cons (assq coding-system ruby-encoding-map)))
+ (if cons (cdr cons) coding-system)))
+ (coding-system
+ (and coding-system
+ (symbol-name coding-system))))
+ (if coding-system
+ (save-excursion
+ (widen)
+ (goto-char (point-min))
+ (if (looking-at "^#!") (beginning-of-line 2))
+ (cond ((looking-at "\\s *#.*-\*-\\s *\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)\\s *\\(;\\|-\*-\\)")
+ (unless (string= (match-string 2) coding-system)
+ (goto-char (match-beginning 2))
+ (delete-region (point) (match-end 2))
+ (and (looking-at "-\*-")
+ (let ((n (skip-chars-backward " ")))
+ (cond ((= n 0) (insert " ") (backward-char))
+ ((= n -1) (insert " "))
+ ((forward-char)))))
+ (insert coding-system)))
+ ((looking-at "\\s *#.*coding\\s *[:=]"))
+ (t (when ruby-insert-encoding-magic-comment
+ (insert "# -*- coding: " coding-system " -*-\n"))))))))
+
+(defun ruby-current-indentation ()
+ (save-excursion
+ (beginning-of-line)
+ (back-to-indentation)
+ (current-column)))
+
+(defun ruby-indent-line (&optional flag)
+ "Correct indentation of the current ruby line."
+ (ruby-indent-to (ruby-calculate-indent)))
+
+(defun ruby-indent-command ()
+ (interactive)
+ (ruby-indent-line t))
+
+(defun ruby-indent-to (x)
+ (if x
+ (let (shift top beg)
+ (and (< x 0) (error "invalid nest"))
+ (setq shift (current-column))
+ (beginning-of-line)
+ (setq beg (point))
+ (back-to-indentation)
+ (setq top (current-column))
+ (skip-chars-backward " \t")
+ (if (>= shift top) (setq shift (- shift top))
+ (setq shift 0))
+ (if (and (bolp)
+ (= x top))
+ (move-to-column (+ x shift))
+ (move-to-column top)
+ (delete-region beg (point))
+ (beginning-of-line)
+ (indent-to x)
+ (move-to-column (+ x shift))))))
+
+(defun ruby-special-char-p (&optional pnt)
+ (setq pnt (or pnt (point)))
+ (let ((c (char-before pnt)) (b (and (< (point-min) pnt) (char-before (1- pnt)))))
+ (cond ((or (eq c ??) (eq c ?$)))
+ ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? ))))
+ ((eq c ?\\) (eq b ??)))))
+
+(defun ruby-singleton-class-p ()
+ (save-excursion
+ (forward-word -1)
+ (and (or (bolp) (not (eq (char-before (point)) ?_)))
+ (looking-at "class\\s *<<"))))
+
+(defun ruby-expr-beg (&optional option)
+ (save-excursion
+ (store-match-data nil)
+ (let ((space (skip-chars-backward " \t"))
+ (start (point)))
+ (cond
+ ((bolp) t)
+ ((progn
+ (forward-char -1)
+ (and (looking-at "\\?")
+ (or (eq (char-syntax (preceding-char)) ?w)
+ (ruby-special-char-p))))
+ nil)
+ ((and (eq option 'heredoc) (< space 0))
+ (not (progn (goto-char start) (ruby-singleton-class-p))))
+ ((or (looking-at ruby-operator-re)
+ (looking-at "[\\[({,;]")
+ (and (looking-at "[!?]")
+ (or (not (eq option 'modifier))
+ (bolp)
+ (save-excursion (forward-char -1) (looking-at "\\Sw$"))))
+ (and (looking-at ruby-symbol-re)
+ (skip-chars-backward ruby-symbol-chars)
+ (cond
+ ((looking-at (regexp-opt
+ (append ruby-block-beg-keywords
+ ruby-block-op-keywords
+ ruby-block-mid-keywords)
+ 'words))
+ (goto-char (match-end 0))
+ (not (looking-at "\\s_\\|[!?:]")))
+ ((eq option 'expr-qstr)
+ (looking-at "[a-zA-Z][a-zA-z0-9_]* +%[^ \t]"))
+ ((eq option 'expr-re)
+ (looking-at "[a-zA-Z][a-zA-z0-9_]* +/[^ \t]"))
+ (t nil)))))))))
+
+(defun ruby-forward-string (term &optional end no-error expand)
+ (let ((n 1) (c (string-to-char term))
+ (re (if expand
+ (concat "[^\\]\\(\\\\\\\\\\)*\\([" term "]\\|\\(#{\\)\\)")
+ (concat "[^\\]\\(\\\\\\\\\\)*[" term "]"))))
+ (while (and (re-search-forward re end no-error)
+ (if (match-beginning 3)
+ (ruby-forward-string "}{" end no-error nil)
+ (> (setq n (if (eq (char-before (point)) c)
+ (1- n) (1+ n))) 0)))
+ (forward-char -1))
+ (cond ((zerop n))
+ (no-error nil)
+ ((error "unterminated string")))))
+
+(defun ruby-deep-indent-paren-p (c &optional pos)
+ (cond ((save-excursion
+ (if pos (goto-char pos))
+ (ruby-expr-beg))
+ nil)
+ ((listp ruby-deep-indent-paren)
+ (let ((deep (assoc c ruby-deep-indent-paren)))
+ (cond (deep
+ (or (cdr deep) ruby-deep-indent-paren-style))
+ ((memq c ruby-deep-indent-paren)
+ ruby-deep-indent-paren-style))))
+ ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style)
+ ((eq c ?\( ) ruby-deep-arglist)))
+
+(defun ruby-parse-partial (&optional end in-string nest depth pcol indent)
+ (or depth (setq depth 0))
+ (or indent (setq indent 0))
+ (when (re-search-forward ruby-delimiter end 'move)
+ (let ((pnt (point)) w re expand)
+ (goto-char (match-beginning 0))
+ (cond
+ ((and (memq (char-before) '(?@ ?$)) (looking-at "\\sw"))
+ (goto-char pnt))
+ ((looking-at "[\"`]") ;skip string
+ (cond
+ ((and (not (eobp))
+ (ruby-forward-string (buffer-substring (point) (1+ (point))) end t t))
+ nil)
+ (t
+ (setq in-string (point))
+ (goto-char end))))
+ ((looking-at "'")
+ (cond
+ ((and (not (eobp))
+ (re-search-forward "[^\\]\\(\\\\\\\\\\)*'" end t))
+ nil)
+ (t
+ (setq in-string (point))
+ (goto-char end))))
+ ((looking-at "/=")
+ (goto-char pnt))
+ ((looking-at "/")
+ (cond
+ ((and (not (eobp)) (ruby-expr-beg 'expr-re))
+ (if (ruby-forward-string "/" end t t)
+ nil
+ (setq in-string (point))
+ (goto-char end)))
+ (t
+ (goto-char pnt))))
+ ((looking-at "%")
+ (cond
+ ((and (not (eobp))
+ (ruby-expr-beg 'expr-qstr)
+ (not (looking-at "%="))
+ (looking-at "%[QqrxWw]?\\([^a-zA-Z0-9 \t\n]\\)"))
+ (goto-char (match-beginning 1))
+ (setq expand (not (memq (char-before) '(?q ?w))))
+ (setq w (match-string 1))
+ (cond
+ ((string= w "[") (setq re "]["))
+ ((string= w "{") (setq re "}{"))
+ ((string= w "(") (setq re ")("))
+ ((string= w "<") (setq re "><"))
+ ((and expand (string= w "\\"))
+ (setq w (concat "\\" w))))
+ (unless (cond (re (ruby-forward-string re end t expand))
+ (expand (ruby-forward-string w end t t))
+ (t (re-search-forward
+ (if (string= w "\\")
+ "\\\\[^\\]*\\\\"
+ (concat "[^\\]\\(\\\\\\\\\\)*" w))
+ end t)))
+ (setq in-string (point))
+ (goto-char end)))
+ (t
+ (goto-char pnt))))
+ ((looking-at "\\?") ;skip ?char
+ (cond
+ ((and (ruby-expr-beg)
+ (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?."))
+ (goto-char (match-end 0)))
+ (t
+ (goto-char pnt))))
+ ((looking-at "\\$") ;skip $char
+ (goto-char pnt)
+ (forward-char 1))
+ ((looking-at "#") ;skip comment
+ (forward-line 1)
+ (goto-char (point))
+ )
+ ((looking-at "[\\[{(]")
+ (let ((deep (ruby-deep-indent-paren-p (char-after))))
+ (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg)))
+ (progn
+ (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]")
+ (setq pnt (1- (match-end 0))))
+ (setq nest (cons (cons (char-after (point)) (point)) nest))
+ (setq pcol (cons (cons pnt depth) pcol))
+ (setq depth 0))
+ (setq nest (cons (cons (char-after (point)) pnt) nest))
+ (setq depth (1+ depth))))
+ (goto-char pnt)
+ )
+ ((looking-at "[])}]")
+ (if (ruby-deep-indent-paren-p (matching-paren (char-after))
+ (if nest
+ (cdr (nth 0 nest))
+ (save-excursion
+ (forward-char)
+ (ruby-backward-sexp)
+ (point))))
+ (setq depth (cdr (car pcol)) pcol (cdr pcol))
+ (setq depth (1- depth)))
+ (setq nest (cdr nest))
+ (goto-char pnt))
+ ((looking-at ruby-block-end-re)
+ (if (or (and (not (bolp))
+ (progn
+ (forward-char -1)
+ (setq w (char-after (point)))
+ (or (eq ?_ w)
+ (eq ?. w))))
+ (progn
+ (goto-char pnt)
+ (setq w (char-after (point)))
+ (or (eq ?_ w)
+ (eq ?! w)
+ (eq ?? w))))
+ nil
+ (setq nest (cdr nest))
+ (setq depth (1- depth)))
+ (goto-char pnt))
+ ((looking-at "def\\s +[^(\n;]*")
+ (if (or (bolp)
+ (progn
+ (forward-char -1)
+ (not (eq ?_ (char-after (point))))))
+ (progn
+ (setq nest (cons (cons nil pnt) nest))
+ (setq depth (1+ depth))))
+ (goto-char (match-end 0)))
+ ((looking-at (concat "\\_<\\(" ruby-block-beg-re "\\)\\_>"))
+ (and
+ (save-match-data
+ (or (not (looking-at (concat "do" ruby-keyword-end-re)))
+ (save-excursion
+ (back-to-indentation)
+ (not (looking-at ruby-non-block-do-re)))))
+ (or (bolp)
+ (progn
+ (forward-char -1)
+ (setq w (char-after (point)))
+ (not (or (eq ?_ w)
+ (eq ?. w)))))
+ (goto-char pnt)
+ (setq w (char-after (point)))
+ (not (eq ?_ w))
+ (not (eq ?! w))
+ (not (eq ?? w))
+ (not (eq ?: w))
+ (skip-chars-forward " \t")
+ (goto-char (match-beginning 0))
+ (or (not (looking-at ruby-modifier-re))
+ (ruby-expr-beg 'modifier))
+ (goto-char pnt)
+ (setq nest (cons (cons nil pnt) nest))
+ (setq depth (1+ depth)))
+ (goto-char pnt))
+ ((looking-at ":\\(['\"]\\)")
+ (goto-char (match-beginning 1))
+ (ruby-forward-string (buffer-substring (match-beginning 1) (match-end 1)) end))
+ ((looking-at ":\\([-,.+*/%&|^~<>]=?\\|===?\\|<=>\\|![~=]?\\)")
+ (goto-char (match-end 0)))
+ ((looking-at ":\\([a-zA-Z_][a-zA-Z_0-9]*[!?=]?\\)?")
+ (goto-char (match-end 0)))
+ ((or (looking-at "\\.\\.\\.?")
+ (looking-at "\\.[0-9]+")
+ (looking-at "\\.[a-zA-Z_0-9]+")
+ (looking-at "\\."))
+ (goto-char (match-end 0)))
+ ((looking-at "^=begin")
+ (if (re-search-forward "^=end" end t)
+ (forward-line 1)
+ (setq in-string (match-end 0))
+ (goto-char end)))
+ ((looking-at "<<")
+ (cond
+ ((and (ruby-expr-beg 'heredoc)
+ (looking-at "<<\\([-~]\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)"))
+ (setq re (regexp-quote (or (match-string 4) (match-string 2))))
+ (if (match-beginning 1) (setq re (concat "\\s *" re)))
+ (let* ((id-end (goto-char (match-end 0)))
+ (line-end-position (save-excursion (end-of-line) (point)))
+ (state (list in-string nest depth pcol indent)))
+ ;; parse the rest of the line
+ (while (and (> line-end-position (point))
+ (setq state (apply 'ruby-parse-partial
+ line-end-position state))))
+ (setq in-string (car state)
+ nest (nth 1 state)
+ depth (nth 2 state)
+ pcol (nth 3 state)
+ indent (nth 4 state))
+ ;; skip heredoc section
+ (if (re-search-forward (concat "^" re "$") end 'move)
+ (forward-line 1)
+ (setq in-string id-end)
+ (goto-char end))))
+ (t
+ (goto-char pnt))))
+ ((looking-at "^__END__$")
+ (goto-char pnt))
+ ((looking-at ruby-here-doc-beg-re)
+ (if (re-search-forward (ruby-here-doc-end-match)
+ ruby-indent-point t)
+ (forward-line 1)
+ (setq in-string (match-end 0))
+ (goto-char ruby-indent-point)))
+ (t
+ (error (format "bad string %s"
+ (buffer-substring (point) pnt)
+ ))))))
+ (list in-string nest depth pcol))
+
+(defun ruby-parse-region (start end)
+ (let (state)
+ (save-excursion
+ (if start
+ (goto-char start)
+ (ruby-beginning-of-indent))
+ (save-restriction
+ (narrow-to-region (point) end)
+ (while (and (> end (point))
+ (setq state (apply 'ruby-parse-partial end state))))))
+ (list (nth 0 state) ; in-string
+ (car (nth 1 state)) ; nest
+ (nth 2 state) ; depth
+ (car (car (nth 3 state))) ; pcol
+ ;(car (nth 5 state)) ; indent
+ )))
+
+(defun ruby-indent-size (pos nest)
+ (+ pos (* (or nest 1) ruby-indent-level)))
+
+(defun ruby-calculate-indent (&optional parse-start)
+ (save-excursion
+ (beginning-of-line)
+ (let ((ruby-indent-point (point))
+ (case-fold-search nil)
+ state bol eol begin op-end
+ (paren (progn (skip-syntax-forward " ")
+ (and (char-after) (matching-paren (char-after)))))
+ (indent 0))
+ (if parse-start
+ (goto-char parse-start)
+ (ruby-beginning-of-indent)
+ (setq parse-start (point)))
+ (back-to-indentation)
+ (setq indent (current-column))
+ (setq state (ruby-parse-region parse-start ruby-indent-point))
+ (cond
+ ((nth 0 state) ; within string
+ (setq indent nil)) ; do nothing
+ ((car (nth 1 state)) ; in paren
+ (goto-char (setq begin (cdr (nth 1 state))))
+ (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state))
+ (1- (cdr (nth 1 state))))))
+ (if deep
+ (cond ((and (eq deep t) (eq (car (nth 1 state)) paren))
+ (skip-syntax-backward " ")
+ (setq indent (1- (current-column))))
+ ((eq deep 'space)
+ (goto-char (cdr (nth 1 state)))
+ (setq indent (1+ (current-column))))
+ ((let ((s (ruby-parse-region (point) ruby-indent-point)))
+ (and (nth 2 s) (> (nth 2 s) 0)
+ (or (goto-char (cdr (nth 1 s))) t)))
+ (forward-word -1)
+ (setq indent (ruby-indent-size (current-column) (nth 2 state))))
+ (t
+ (setq indent (current-column))
+ (cond ((eq deep 'space))
+ (paren (setq indent (1- indent)))
+ (t (setq indent (ruby-indent-size (1- indent) 1))))))
+ (if (nth 3 state) (goto-char (nth 3 state))
+ (goto-char parse-start) (back-to-indentation))
+ (setq indent (ruby-indent-size (current-column) (nth 2 state))))
+ (and (eq (car (nth 1 state)) paren)
+ (ruby-deep-indent-paren-p (matching-paren paren)
+ (1- (cdr (nth 1 state))))
+ (search-backward (char-to-string paren))
+ (setq indent (current-column)))))
+ ((and (nth 2 state) (> (nth 2 state) 0)) ; in nest
+ (if (null (cdr (nth 1 state)))
+ (error "invalid nest"))
+ (goto-char (cdr (nth 1 state)))
+ (forward-word -1) ; skip back a keyword
+ (setq begin (point))
+ (cond
+ ((looking-at "do\\>[^_]") ; iter block is a special case
+ (if (nth 3 state) (goto-char (nth 3 state))
+ (goto-char parse-start) (back-to-indentation))
+ (setq indent (ruby-indent-size (current-column) (nth 2 state))))
+ (t
+ (setq indent (+ (current-column) ruby-indent-level)))))
+
+ ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest
+ (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
+ (when indent
+ (goto-char ruby-indent-point)
+ (end-of-line)
+ (setq eol (point))
+ (beginning-of-line)
+ (cond
+ ((and (not (ruby-deep-indent-paren-p paren
+ (and (cdr (nth 1 state))
+ (1- (cdr (nth 1 state))))))
+ (re-search-forward ruby-negative eol t))
+ (and (not (eq ?_ (char-after (match-end 0))))
+ (setq indent (- indent ruby-indent-level))))
+ ((and
+ (save-excursion
+ (beginning-of-line)
+ (not (bobp)))
+ (or (ruby-deep-indent-paren-p t)
+ (null (car (nth 1 state)))))
+ ;; goto beginning of non-empty no-comment line
+ (let (end done)
+ (while (not done)
+ (skip-chars-backward " \t\n")
+ (setq end (point))
+ (beginning-of-line)
+ (if (re-search-forward "^\\s *#" end t)
+ (beginning-of-line)
+ (setq done t))))
+ (setq bol (point))
+ (end-of-line)
+ ;; skip the comment at the end
+ (skip-chars-backward " \t")
+ (let (end (pos (point)))
+ (beginning-of-line)
+ (while (and (re-search-forward "#" pos t)
+ (setq end (1- (point)))
+ (or (ruby-special-char-p end)
+ (and (setq state (ruby-parse-region parse-start end))
+ (nth 0 state))))
+ (setq end nil))
+ (goto-char (or end pos))
+ (skip-chars-backward " \t")
+ (setq begin (if (and end (nth 0 state)) pos (cdr (nth 1 state))))
+ (setq state (ruby-parse-region parse-start (point))))
+ (or (bobp) (forward-char -1))
+ (and
+ (or (and (looking-at ruby-symbol-re)
+ (skip-chars-backward ruby-symbol-chars)
+ (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>"))
+ (not (eq (point) (nth 3 state)))
+ (save-excursion
+ (goto-char (match-end 0))
+ (not (looking-at "[a-z_]"))))
+ (and (looking-at ruby-operator-re)
+ (not (ruby-special-char-p))
+ ;; operator at the end of line
+ (let ((c (char-after (point))))
+ (and
+;; (or (null begin)
+;; (save-excursion
+;; (goto-char begin)
+;; (skip-chars-forward " \t")
+;; (not (or (eolp) (looking-at "#")
+;; (and (eq (car (nth 1 state)) ?{)
+;; (looking-at "|"))))))
+ (or (not (eq ?/ c))
+ (null (nth 0 (ruby-parse-region (or begin parse-start) (point)))))
+ (or (not (eq ?| (char-after (point))))
+ (save-excursion
+ (or (eolp) (forward-char -1))
+ (cond
+ ((search-backward "|" nil t)
+ (skip-chars-backward " \t\n")
+ (and (not (eolp))
+ (progn
+ (forward-char -1)
+ (not (looking-at "{")))
+ (progn
+ (forward-word -1)
+ (not (looking-at "do\\>[^_]")))))
+ (t t))))
+ (not (eq ?, c))
+ (setq op-end t)))))
+ (setq indent
+ (cond
+ ((and
+ (null op-end)
+ (not (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>")))
+ (eq (ruby-deep-indent-paren-p t) 'space)
+ (not (bobp)))
+ (widen)
+ (goto-char (or begin parse-start))
+ (skip-syntax-forward " ")
+ (current-column))
+ ((car (nth 1 state)) indent)
+ (t
+ (+ indent ruby-indent-level))))))))
+ (goto-char ruby-indent-point)
+ (beginning-of-line)
+ (skip-syntax-forward " ")
+ (if (looking-at "\\.[^.]\\|&\\.")
+ (+ indent ruby-indent-level)
+ indent))))
+
+(defun ruby-electric-brace (arg)
+ (interactive "P")
+ (insert-char last-command-event 1)
+ (ruby-indent-line t)
+ (delete-char -1)
+ (self-insert-command (prefix-numeric-value arg)))
+
+(eval-when-compile
+ (defmacro defun-region-command (func args &rest body)
+ (let ((intr (car body)))
+ (when (featurep 'xemacs)
+ (if (stringp intr) (setq intr (cadr body)))
+ (and (eq (car intr) 'interactive)
+ (setq intr (cdr intr))
+ (setcar intr (concat "_" (car intr)))))
+ (cons 'defun (cons func (cons args body))))))
+
+(defun-region-command ruby-beginning-of-defun (&optional arg)
+ "Move backward to next beginning-of-defun.
+With argument, do this that many times.
+Returns t unless search stops due to end of buffer."
+ (interactive "p")
+ (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\_>")
+ nil 'move (or arg 1))
+ (progn (beginning-of-line) t)))
+
+(defun ruby-beginning-of-indent ()
+ (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\_>")
+ nil 'move)
+ (progn
+ (beginning-of-line)
+ t)))
+
+(defun-region-command ruby-end-of-defun (&optional arg)
+ "Move forward to next end of defun.
+An end of a defun is found by moving forward from the beginning of one."
+ (interactive "p")
+ (and (re-search-forward (concat "^\\(" ruby-block-end-re "\\)\\($\\|\\b[^_]\\)")
+ nil 'move (or arg 1))
+ (progn (beginning-of-line) t))
+ (forward-line 1))
+
+(defun ruby-move-to-block (n)
+ (let (start pos done down (orig (point)))
+ (setq start (ruby-calculate-indent))
+ (setq down (looking-at (if (< n 0) ruby-block-end-re
+ (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))))
+ (while (and (not done) (not (if (< n 0) (bobp) (eobp))))
+ (forward-line n)
+ (cond
+ ((looking-at "^\\s *$"))
+ ((looking-at "^\\s *#"))
+ ((and (> n 0) (looking-at "^=begin\\>"))
+ (re-search-forward "^=end\\>"))
+ ((and (< n 0) (looking-at "^=end\\>"))
+ (re-search-backward "^=begin\\>"))
+ (t
+ (setq pos (current-indentation))
+ (cond
+ ((< start pos)
+ (setq down t))
+ ((and down (= pos start))
+ (setq done t))
+ ((> start pos)
+ (setq done t)))))
+ (if done
+ (save-excursion
+ (back-to-indentation)
+ (if (looking-at (concat "\\<\\(" ruby-block-mid-re "\\)\\>"))
+ (setq done nil)))))
+ (back-to-indentation)
+ (when (< n 0)
+ (let ((eol (point-at-eol)) state next)
+ (if (< orig eol) (setq eol orig))
+ (setq orig (point))
+ (while (and (setq next (apply 'ruby-parse-partial eol state))
+ (< (point) eol))
+ (setq state next))
+ (when (cdaadr state)
+ (goto-char (cdaadr state)))
+ (backward-word)))))
+
+(defun-region-command ruby-beginning-of-block (&optional arg)
+ "Move backward to next beginning-of-block"
+ (interactive "p")
+ (ruby-move-to-block (- (or arg 1))))
+
+(defun-region-command ruby-end-of-block (&optional arg)
+ "Move forward to next beginning-of-block"
+ (interactive "p")
+ (ruby-move-to-block (or arg 1)))
+
+(defun-region-command ruby-forward-sexp (&optional cnt)
+ (interactive "p")
+ (if (and (numberp cnt) (< cnt 0))
+ (ruby-backward-sexp (- cnt))
+ (let ((i (or cnt 1)))
+ (condition-case nil
+ (while (> i 0)
+ (skip-syntax-forward " ")
+ (if (looking-at ",\\s *") (goto-char (match-end 0)))
+ (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ")
+ (goto-char (match-end 0)))
+ ((progn
+ (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*")
+ (looking-at "\\s("))
+ (goto-char (scan-sexps (point) 1)))
+ ((and (looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))
+ (not (eq (char-before (point)) ?.))
+ (not (eq (char-before (point)) ?:)))
+ (ruby-end-of-block)
+ (forward-word 1))
+ ((looking-at "\\(\\$\\|@@?\\)?\\sw")
+ (while (progn
+ (while (progn (forward-word 1) (looking-at "_")))
+ (cond ((looking-at "::") (forward-char 2) t)
+ ((> (skip-chars-forward ".") 0))
+ ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)")
+ (forward-char 1) nil)))))
+ ((let (state expr)
+ (while
+ (progn
+ (setq expr (or expr (ruby-expr-beg)
+ (looking-at "%\\sw?\\Sw\\|[\"'`/]")))
+ (nth 1 (setq state (apply 'ruby-parse-partial nil state))))
+ (setq expr t)
+ (skip-chars-forward "<"))
+ (not expr))))
+ (setq i (1- i)))
+ ((error) (forward-word 1)))
+ i)))
+
+(defun-region-command ruby-backward-sexp (&optional cnt)
+ (interactive "p")
+ (if (and (numberp cnt) (< cnt 0))
+ (ruby-forward-sexp (- cnt))
+ (let ((i (or cnt 1)))
+ (condition-case nil
+ (while (> i 0)
+ (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*")
+ (forward-char -1)
+ (cond ((looking-at "\\s)")
+ (goto-char (scan-sexps (1+ (point)) -1))
+ (case (char-before)
+ (?% (forward-char -1))
+ ('(?q ?Q ?w ?W ?r ?x)
+ (if (eq (char-before (1- (point))) ?%) (forward-char -2))))
+ nil)
+ ((looking-at "\\s\"\\|\\\\\\S_")
+ (let ((c (char-to-string (char-before (match-end 0)))))
+ (while (and (search-backward c)
+ (oddp (skip-chars-backward "\\")))))
+ nil)
+ ((looking-at "\\s.\\|\\s\\")
+ (if (ruby-special-char-p) (forward-char -1)))
+ ((looking-at "\\s(") nil)
+ (t
+ (forward-char 1)
+ (while (progn (forward-word -1)
+ (case (char-before)
+ (?_ t)
+ (?. (forward-char -1) t)
+ ((?$ ?@)
+ (forward-char -1)
+ (and (eq (char-before) (char-after)) (forward-char -1)))
+ (?:
+ (forward-char -1)
+ (eq (char-before) :)))))
+ (if (looking-at ruby-block-end-re)
+ (ruby-beginning-of-block))
+ nil))
+ (setq i (1- i)))
+ ((error)))
+ i)))
+
+(defun ruby-reindent-then-newline-and-indent ()
+ (interactive "*")
+ (newline)
+ (save-excursion
+ (end-of-line 0)
+ (indent-according-to-mode)
+ (delete-region (point) (progn (skip-chars-backward " \t") (point))))
+ (indent-according-to-mode))
+
+(fset 'ruby-encomment-region (symbol-function 'comment-region))
+
+(defun ruby-decomment-region (beg end)
+ (interactive "r")
+ (save-excursion
+ (goto-char beg)
+ (while (re-search-forward "^\\([ \t]*\\)#" end t)
+ (replace-match "\\1" nil nil)
+ (save-excursion
+ (ruby-indent-line)))))
+
+(defun ruby-insert-end ()
+ (interactive)
+ (insert "end")
+ (ruby-indent-line t)
+ (end-of-line))
+
+(defun ruby-mark-defun ()
+ "Put mark at end of this Ruby function, point at beginning."
+ (interactive)
+ (push-mark (point))
+ (ruby-end-of-defun)
+ (push-mark (point) nil t)
+ (ruby-beginning-of-defun)
+ (re-search-backward "^\n" (- (point) 1) t))
+
+(defun ruby-indent-exp (&optional shutup-p)
+ "Indent each line in the balanced expression following point syntactically.
+If optional SHUTUP-P is non-nil, no errors are signalled if no
+balanced expression is found."
+ (interactive "*P")
+ (let ((here (point-marker)) start top column (nest t))
+ (set-marker-insertion-type here t)
+ (unwind-protect
+ (progn
+ (beginning-of-line)
+ (setq start (point) top (current-indentation))
+ (while (and (not (eobp))
+ (progn
+ (setq column (ruby-calculate-indent start))
+ (cond ((> column top)
+ (setq nest t))
+ ((and (= column top) nest)
+ (setq nest nil) t))))
+ (ruby-indent-to column)
+ (beginning-of-line 2)))
+ (goto-char here)
+ (set-marker here nil))))
+
+(defun ruby-add-log-current-method ()
+ "Return current method string."
+ (condition-case nil
+ (save-excursion
+ (let (mname mlist (indent 0))
+ ;; get current method (or class/module)
+ (if (re-search-backward
+ (concat "^[ \t]*\\(def\\|class\\|module\\)[ \t]+"
+ "\\("
+ ;; \\. and :: for class method
+ "\\([A-Za-z_]" ruby-symbol-re "*\\|\\.\\|::" "\\)"
+ "+\\)")
+ nil t)
+ (progn
+ (setq mname (match-string 2))
+ (unless (string-equal "def" (match-string 1))
+ (setq mlist (list mname) mname nil))
+ (goto-char (match-beginning 1))
+ (setq indent (current-column))
+ (beginning-of-line)))
+ ;; nest class/module
+ (while (and (> indent 0)
+ (re-search-backward
+ (concat
+ "^[ \t]*\\(class\\|module\\)[ \t]+"
+ "\\([A-Z]" ruby-symbol-re "*\\)")
+ nil t))
+ (goto-char (match-beginning 1))
+ (if (< (current-column) indent)
+ (progn
+ (setq mlist (cons (match-string 2) mlist))
+ (setq indent (current-column))
+ (beginning-of-line))))
+ (when mname
+ (let ((mn (split-string mname "\\.\\|::")))
+ (if (cdr mn)
+ (progn
+ (cond
+ ((string-equal "" (car mn))
+ (setq mn (cdr mn) mlist nil))
+ ((string-equal "self" (car mn))
+ (setq mn (cdr mn)))
+ ((let ((ml (nreverse mlist)))
+ (while ml
+ (if (string-equal (car ml) (car mn))
+ (setq mlist (nreverse (cdr ml)) ml nil))
+ (or (setq ml (cdr ml)) (nreverse mlist))))))
+ (if mlist
+ (setcdr (last mlist) mn)
+ (setq mlist mn))
+ (setq mn (last mn 2))
+ (setq mname (concat "." (cadr mn)))
+ (setcdr mn nil))
+ (setq mname (concat "#" mname)))))
+ ;; generate string
+ (if (consp mlist)
+ (setq mlist (mapconcat (function identity) mlist "::")))
+ (if mname
+ (if mlist (concat mlist mname) mname)
+ mlist)))))
+
+(defun ruby-brace-to-do-end ()
+ (when (looking-at "{")
+ (let ((orig (point)) (end (progn (ruby-forward-sexp) (point)))
+ oneline (end (make-marker)))
+ (setq oneline (and (eolp) (<= (point-at-bol) orig)))
+ (when (eq (char-before) ?\})
+ (delete-char -1)
+ (cond
+ (oneline
+ (insert "\n")
+ (set-marker end (point)))
+ ((eq (char-syntax (preceding-char)) ?w)
+ (insert " ")))
+ (insert "end")
+ (if (eq (char-syntax (following-char)) ?w)
+ (insert " "))
+ (goto-char orig)
+ (delete-char 1)
+ (if (eq (char-syntax (preceding-char)) ?w)
+ (insert " "))
+ (insert "do")
+ (when (looking-at "\\sw\\||")
+ (insert " ")
+ (backward-char))
+ (when oneline
+ (setq orig (point))
+ (when (cond
+ ((looking-at "\\s *|")
+ (goto-char (match-end 0))
+ (and (search-forward "|" (point-at-eol) 'move)
+ (not (eolp))))
+ (t))
+ (while (progn
+ (insert "\n")
+ (ruby-forward-sexp)
+ (looking-at "\\s *;\\s *"))
+ (delete-char (- (match-end 0) (match-beginning 0))))
+ (goto-char orig)
+ (beginning-of-line 2)
+ (indent-region (point) end))
+ (goto-char orig))
+ t))))
+
+(defun ruby-do-end-to-brace ()
+ (when (and (or (bolp)
+ (not (memq (char-syntax (preceding-char)) '(?w ?_))))
+ (looking-at "\\<do\\(\\s \\|$\\)"))
+ (let ((orig (point)) (end (progn (ruby-forward-sexp) (point)))
+ first last)
+ (backward-char 3)
+ (when (looking-at ruby-block-end-re)
+ (delete-char 3)
+ (insert "}")
+ (setq last (and (eolp)
+ (progn (backward-char 1)
+ (skip-syntax-backward " ")
+ (bolp))
+ (1- (point-at-eol -1))))
+ (goto-char orig)
+ (delete-char 2)
+ (insert "{")
+ (setq orig (point))
+ (when (and last (<= last (point))
+ (not (search-forward "#" (setq first (point-at-eol)) t)))
+ (goto-char (- end 4))
+ (end-of-line 0)
+ (if (looking-at "\n\\s *")
+ (delete-char (- (match-end 0) (match-beginning 0))) t)
+ (goto-char first)
+ (if (looking-at "\n\\s *")
+ (delete-char (- (match-end 0) (match-beginning 0))) t))
+ (goto-char orig)
+ (if (looking-at "\\s +|")
+ (delete-char (- (match-end 0) (match-beginning 0) 1)))
+ t))))
+
+(defun ruby-toggle-block ()
+ (interactive)
+ (or (ruby-brace-to-do-end)
+ (ruby-do-end-to-brace)))
+
+(eval-when-compile
+ (if (featurep 'font-lock)
+ (defmacro eval-when-font-lock-available (&rest args) (cons 'progn args))
+ (defmacro eval-when-font-lock-available (&rest args))))
+
+(eval-when-compile
+ (if (featurep 'hilit19)
+ (defmacro eval-when-hilit19-available (&rest args) (cons 'progn args))
+ (defmacro eval-when-hilit19-available (&rest args))))
+
+(eval-when-font-lock-available
+ (or (boundp 'font-lock-variable-name-face)
+ (setq font-lock-variable-name-face font-lock-type-face))
+
+ (defconst ruby-font-lock-syntactic-keywords
+ `(
+ ;; #{ }, #$hoge, #@foo are not comments
+ ("\\(#\\)[{$@]" 1 (1 . nil))
+ ;; the last $', $", $` in the respective string is not variable
+ ;; the last ?', ?", ?` in the respective string is not ascii code
+ ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
+ (2 (7 . nil))
+ (4 (7 . nil)))
+ ;; $' $" $` .... are variables
+ ;; ?' ?" ?` are ascii codes
+ ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
+ ;; regexps
+ ("\\(^\\|[[{|=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
+ (4 (7 . ?/))
+ (6 (7 . ?/)))
+ ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil))
+ ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil))
+ (,(concat ruby-here-doc-beg-re ".*\\(\n\\)")
+ ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re))
+ (ruby-here-doc-beg-syntax))
+ (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax))))
+
+ (unless (functionp 'syntax-ppss)
+ (defun syntax-ppss (&optional pos)
+ (parse-partial-sexp (point-min) (or pos (point)))))
+
+ (defun ruby-in-ppss-context-p (context &optional ppss)
+ (let ((ppss (or ppss (syntax-ppss (point)))))
+ (if (cond
+ ((eq context 'anything)
+ (or (nth 3 ppss)
+ (nth 4 ppss)))
+ ((eq context 'string)
+ (nth 3 ppss))
+ ((eq context 'heredoc)
+ (and (nth 3 ppss)
+ ;; If it's generic string, it's a heredoc and we don't care
+ ;; See `parse-partial-sexp'
+ (not (numberp (nth 3 ppss)))))
+ ((eq context 'non-heredoc)
+ (and (ruby-in-ppss-context-p 'anything)
+ (not (ruby-in-ppss-context-p 'heredoc))))
+ ((eq context 'comment)
+ (nth 4 ppss))
+ (t
+ (error (concat
+ "Internal error on `ruby-in-ppss-context-p': "
+ "context name `" (symbol-name context) "' is unknown"))))
+ t)))
+
+ (defun ruby-in-here-doc-p ()
+ (save-excursion
+ (let ((old-point (point)) (case-fold-search nil))
+ (beginning-of-line)
+ (catch 'found-beg
+ (while (and (re-search-backward ruby-here-doc-beg-re nil t)
+ (not (ruby-singleton-class-p)))
+ (if (not (or (ruby-in-ppss-context-p 'anything)
+ (ruby-here-doc-find-end old-point)))
+ (throw 'found-beg t)))))))
+
+ (defun ruby-here-doc-find-end (&optional limit)
+ "Expects the point to be on a line with one or more heredoc
+openers. Returns the buffer position at which all heredocs on the
+line are terminated, or nil if they aren't terminated before the
+buffer position `limit' or the end of the buffer."
+ (save-excursion
+ (beginning-of-line)
+ (catch 'done
+ (let ((eol (save-excursion (end-of-line) (point)))
+ (case-fold-search nil)
+ ;; Fake match data such that (match-end 0) is at eol
+ (end-match-data (progn (looking-at ".*$") (match-data)))
+ beg-match-data end-re)
+ (while (re-search-forward ruby-here-doc-beg-re eol t)
+ (setq beg-match-data (match-data))
+ (setq end-re (ruby-here-doc-end-match))
+
+ (set-match-data end-match-data)
+ (goto-char (match-end 0))
+ (unless (re-search-forward end-re limit t) (throw 'done nil))
+ (setq end-match-data (match-data))
+
+ (set-match-data beg-match-data)
+ (goto-char (match-end 0)))
+ (set-match-data end-match-data)
+ (goto-char (match-end 0))
+ (point)))))
+
+ (defun ruby-here-doc-beg-syntax ()
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (unless (or (ruby-in-ppss-context-p 'non-heredoc)
+ (ruby-in-here-doc-p))
+ (string-to-syntax "|"))))
+
+ (defun ruby-here-doc-end-syntax ()
+ (let ((pss (syntax-ppss)) (case-fold-search nil))
+ (when (ruby-in-ppss-context-p 'heredoc pss)
+ (save-excursion
+ (goto-char (nth 8 pss)) ; Go to the beginning of heredoc.
+ (let ((eol (point)))
+ (beginning-of-line)
+ (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line...
+ (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment...
+ (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line...
+ (not (re-search-forward ruby-here-doc-beg-re eol t))))
+ (string-to-syntax "|")))))))
+
+ (eval-when-compile
+ (put 'ruby-mode 'font-lock-defaults
+ '((ruby-font-lock-keywords)
+ nil nil nil
+ beginning-of-line
+ (font-lock-syntactic-keywords
+ . ruby-font-lock-syntactic-keywords))))
+
+ (defun ruby-font-lock-docs (limit)
+ (if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t)
+ (let (beg)
+ (beginning-of-line)
+ (setq beg (point))
+ (forward-line 1)
+ (if (re-search-forward "^=end\\(\\s \\|$\\)" limit t)
+ (progn
+ (set-match-data (list beg (point)))
+ t)))))
+
+ (defun ruby-font-lock-maybe-docs (limit)
+ (let (beg)
+ (save-excursion
+ (if (and (re-search-backward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t)
+ (string= (match-string 1) "begin"))
+ (progn
+ (beginning-of-line)
+ (setq beg (point)))))
+ (if (and beg (and (re-search-forward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t)
+ (string= (match-string 1) "end")))
+ (progn
+ (set-match-data (list beg (point)))
+ t)
+ nil)))
+
+ (defvar ruby-font-lock-syntax-table
+ (let* ((tbl (copy-syntax-table ruby-mode-syntax-table)))
+ (modify-syntax-entry ?_ "w" tbl)
+ tbl))
+
+ (defconst ruby-font-lock-keywords
+ (list
+ ;; functions
+ '("^\\s *def\\s +\\([^( \t\n]+\\)"
+ 1 font-lock-function-name-face)
+ ;; keywords
+ (cons (concat
+ "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\_<\\(defined\\?\\|"
+ (regexp-opt
+ '("alias"
+ "and"
+ "begin"
+ "break"
+ "case"
+ "catch"
+ "class"
+ "def"
+ "do"
+ "elsif"
+ "else"
+ "fail"
+ "ensure"
+ "for"
+ "end"
+ "if"
+ "in"
+ "module"
+ "next"
+ "not"
+ "or"
+ "raise"
+ "redo"
+ "rescue"
+ "retry"
+ "return"
+ "then"
+ "throw"
+ "super"
+ "unless"
+ "undef"
+ "until"
+ "when"
+ "while"
+ "yield"
+ )
+ t)
+ "\\)"
+ ruby-keyword-end-re)
+ 2)
+ ;; here-doc beginnings
+ (list ruby-here-doc-beg-re 0 'font-lock-string-face)
+ ;; variables
+ '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\_<\\(nil\\|self\\|true\\|false\\)\\>"
+ 2 font-lock-variable-name-face)
+ ;; variables
+ '("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W"
+ 1 font-lock-variable-name-face)
+ '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+"
+ 0 font-lock-variable-name-face)
+ ;; embedded document
+ '(ruby-font-lock-docs
+ 0 font-lock-comment-face t)
+ '(ruby-font-lock-maybe-docs
+ 0 font-lock-comment-face t)
+ ;; general delimited string
+ '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)"
+ (2 font-lock-string-face))
+ ;; constants
+ '("\\(^\\|[^_]\\)\\_<\\([A-Z]+\\(\\w\\|_\\)*\\)"
+ 2 font-lock-type-face)
+ ;; symbols
+ '("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|![~=]?\\|\\[\\]=?\\|\\(\\w\\|_\\)+\\([!?=]\\|\\b_*\\)\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)"
+ 2 font-lock-reference-face)
+ '("\\(^\\s *\\|[\[\{\(,]\\s *\\|\\sw\\s +\\)\\(\\(\\sw\\|_\\)+\\):[^:]" 2 font-lock-reference-face)
+ ;; expression expansion
+ '("#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)"
+ 0 font-lock-variable-name-face t)
+ ;; warn lower camel case
+ ;'("\\<[a-z]+[a-z0-9]*[A-Z][A-Za-z0-9]*\\([!?]?\\|\\>\\)"
+ ; 0 font-lock-warning-face)
+ )
+ "*Additional expressions to highlight in ruby mode."))
+
+(eval-when-hilit19-available
+ (hilit-set-mode-patterns
+ 'ruby-mode
+ '(("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string)
+ ("[^$\\?]\\('[^\\']*\\(\\\\\\(.\\|\n\\)[^\\']*\\)*'\\)" 1 string)
+ ("[^$\\?]\\(`[^\\`]*\\(\\\\\\(.\\|\n\\)[^\\`]*\\)*`\\)" 1 string)
+ ("^\\s *#.*$" nil comment)
+ ("[^$@?\\]\\(#[^$@{\n].*$\\)" 1 comment)
+ ("[^a-zA-Z_]\\(\\?\\(\\\\[CM]-\\)*.\\)" 1 string)
+ ("^\\s *\\(require\\|load\\).*$" nil include)
+ ("^\\s *\\(include\\|alias\\|undef\\).*$" nil decl)
+ ("^\\s *\\<\\(class\\|def\\|module\\)\\>" "[)\n;]" defun)
+ ("[^_]\\<\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|unless\\|rescue\\|then\\|when\\|while\\|until\\|do\\|yield\\)\\>\\([^_]\\|$\\)" 1 defun)
+ ("[^_]\\<\\(and\\|break\\|next\\|raise\\|fail\\|in\\|not\\|or\\|redo\\|retry\\|return\\|super\\|yield\\|catch\\|throw\\|self\\|nil\\)\\>\\([^_]\\|$\\)" 1 keyword)
+ ("\\$\\(.\\|\\sw+\\)" nil type)
+ ("[$@].[a-zA-Z_0-9]*" nil struct)
+ ("^__END__" nil label))))
+
+
+;;;###autoload
+(defun ruby-mode ()
+ "Major mode for editing ruby scripts.
+\\[ruby-indent-command] properly indents subexpressions of multi-line
+class, module, def, if, while, for, do, and case statements, taking
+nesting into account.
+
+The variable ruby-indent-level controls the amount of indentation.
+\\{ruby-mode-map}"
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map ruby-mode-map)
+ (setq mode-name "Ruby")
+ (setq major-mode 'ruby-mode)
+ (ruby-mode-variables)
+
+ (make-local-variable 'imenu-create-index-function)
+ (setq imenu-create-index-function 'ruby-imenu-create-index)
+
+ (make-local-variable 'add-log-current-defun-function)
+ (setq add-log-current-defun-function 'ruby-add-log-current-method)
+
+ (add-hook
+ (cond ((boundp 'before-save-hook)
+ (make-local-variable 'before-save-hook)
+ 'before-save-hook)
+ ((boundp 'write-contents-functions) 'write-contents-functions)
+ ((boundp 'write-contents-hooks) 'write-contents-hooks))
+ 'ruby-mode-set-encoding)
+
+ (set (make-local-variable 'font-lock-defaults) '((ruby-font-lock-keywords) nil nil))
+ (set (make-local-variable 'font-lock-keywords) ruby-font-lock-keywords)
+ (set (make-local-variable 'font-lock-syntax-table) ruby-font-lock-syntax-table)
+ (set (make-local-variable 'font-lock-syntactic-keywords) ruby-font-lock-syntactic-keywords)
+
+ (if (fboundp 'run-mode-hooks)
+ (run-mode-hooks 'ruby-mode-hook)
+ (run-hooks 'ruby-mode-hook)))
+
+(provide 'ruby-mode)
diff --git a/misc/ruby-style.el b/misc/ruby-style.el
index 03d0830d3a..b8593b202a 100644
--- a/misc/ruby-style.el
+++ b/misc/ruby-style.el
@@ -55,10 +55,8 @@
'("bsd"
(c-basic-offset . 4)
(tab-width . 8)
- (indent-tabs-mode . nil)
- (show-trailing-whitespace . t)
- (c-backslash-column . 1)
- (c-backslash-max-column . 1)
+ (indent-tabs-mode . t)
+ (setq show-trailing-whitespace t)
(c-offsets-alist
(case-label . *)
(label . (ruby-style-label-indent *))
@@ -68,17 +66,6 @@
(access-label /)
)))
-(c-add-style
- "prism"
- '("bsd"
- (c-basic-offset . 4)
- (tab-width . 8)
- (indent-tabs-mode . nil)
- (show-trailing-whitespace . t)
- (c-offsets-alist
- (case-label . +)
- )))
-
;;;###autoload
(defun ruby-style-c-mode ()
(interactive)
@@ -88,20 +75,7 @@
(let ((head (progn (forward-line 100) (point)))
(case-fold-search nil))
(goto-char (point-min))
- (re-search-forward "Copyright (C) .* Yukihiro Matsumoto" head t)))
- (condition-case ()
- (with-temp-buffer
- (when (= 0 (call-process "git" nil t nil "remote" "get-url" "origin"))
- (goto-char (point-min))
- (looking-at ".*/ruby\\(\\.git\\)?$")))
- (error))
- (condition-case ()
- (with-temp-buffer
- (when (= 0 (call-process "svn" nil t nil "info" "--xml"))
- (goto-char (point-min))
- (search-forward-regexp "<root>.*/ruby</root>" nil)))
- (error))
- nil)
+ (re-search-forward "Copyright (C) .* Yukihiro Matsumoto" head t))))
(c-set-style "ruby")))
(provide 'ruby-style)
diff --git a/misc/rubydb2x.el b/misc/rubydb2x.el
new file mode 100644
index 0000000000..a74265fb0e
--- /dev/null
+++ b/misc/rubydb2x.el
@@ -0,0 +1,104 @@
+(require 'gud)
+(provide 'rubydb)
+
+;; ======================================================================
+;; rubydb functions
+
+;;; History of argument lists passed to rubydb.
+(defvar gud-rubydb-history nil)
+
+(defun gud-rubydb-massage-args (file args)
+ (cons "-I" (cons "." (cons "-r" (cons "debug" (cons file args))))))
+
+;; There's no guarantee that Emacs will hand the filter the entire
+;; marker at once; it could be broken up across several strings. We
+;; might even receive a big chunk with several markers in it. If we
+;; receive a chunk of text which looks like it might contain the
+;; beginning of a marker, we save it here between calls to the
+;; filter.
+(defvar gud-rubydb-marker-acc "")
+
+(defun gud-rubydb-marker-filter (string)
+ (save-match-data
+ (setq gud-marker-acc (concat gud-marker-acc string))
+ (let ((output ""))
+
+ ;; Process all the complete markers in this chunk.
+ (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n"
+ gud-marker-acc)
+ (setq
+
+ ;; Extract the frame position from the marker.
+ gud-last-frame
+ (cons (substring gud-marker-acc (match-beginning 1) (match-end 1))
+ (string-to-int (substring gud-marker-acc
+ (match-beginning 2)
+ (match-end 2))))
+
+ ;; Append any text before the marker to the output we're going
+ ;; to return - we don't include the marker in this text.
+ output (concat output
+ (substring gud-marker-acc 0 (match-beginning 0)))
+
+ ;; Set the accumulator to the remaining text.
+ gud-marker-acc (substring gud-marker-acc (match-end 0))))
+
+ ;; Does the remaining text look like it might end with the
+ ;; beginning of another marker? If it does, then keep it in
+ ;; gud-marker-acc until we receive the rest of it. Since we
+ ;; know the full marker regexp above failed, it's pretty simple to
+ ;; test for marker starts.
+ (if (string-match "\032.*\\'" gud-marker-acc)
+ (progn
+ ;; Everything before the potential marker start can be output.
+ (setq output (concat output (substring gud-marker-acc
+ 0 (match-beginning 0))))
+
+ ;; Everything after, we save, to combine with later input.
+ (setq gud-marker-acc
+ (substring gud-marker-acc (match-beginning 0))))
+
+ (setq output (concat output gud-marker-acc)
+ gud-marker-acc ""))
+
+ output)))
+
+(defun gud-rubydb-find-file (f)
+ (find-file-noselect f))
+
+(defvar rubydb-command-name "ruby"
+ "File name for executing ruby.")
+
+;;;###autoload
+(defun rubydb (command-line)
+ "Run rubydb on program FILE in buffer *gud-FILE*.
+The directory containing FILE becomes the initial working directory
+and source-file directory for your debugger."
+ (interactive
+ (list (read-from-minibuffer "Run rubydb (like this): "
+ (if (consp gud-rubydb-history)
+ (car gud-rubydb-history)
+ (concat rubydb-command-name " "))
+ nil nil
+ '(gud-rubydb-history . 1))))
+
+ (gud-overload-functions '((gud-massage-args . gud-rubydb-massage-args)
+ (gud-marker-filter . gud-rubydb-marker-filter)
+ (gud-find-file . gud-rubydb-find-file)
+ ))
+ (gud-common-init command-line)
+
+ (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.")
+; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
+ (gud-def gud-step "s" "\C-s" "Step one source line with display.")
+ (gud-def gud-next "n" "\C-n" "Step one line (skip functions).")
+ (gud-def gud-cont "c" "\C-r" "Continue with display.")
+ (gud-def gud-finish "finish" "\C-f" "Finish executing current function.")
+ (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).")
+ (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).")
+ (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.")
+
+ (setq comint-prompt-regexp "^(rdb:-) ")
+ (setq paragraph-start comint-prompt-regexp)
+ (run-hooks 'rubydb-mode-hook)
+ )
diff --git a/misc/rubydb3x.el b/misc/rubydb3x.el
new file mode 100644
index 0000000000..9d6bc57d5a
--- /dev/null
+++ b/misc/rubydb3x.el
@@ -0,0 +1,115 @@
+(require 'gud)
+(provide 'rubydb)
+
+;; ======================================================================
+;; rubydb functions
+
+;;; History of argument lists passed to rubydb.
+(defvar gud-rubydb-history nil)
+
+(if (fboundp 'gud-overload-functions)
+ (defun gud-rubydb-massage-args (file args)
+ (cons "-r" (cons "debug" (cons file args))))
+ (defun gud-rubydb-massage-args (file args)
+ (cons "-r" (cons "debug" args))))
+
+;; There's no guarantee that Emacs will hand the filter the entire
+;; marker at once; it could be broken up across several strings. We
+;; might even receive a big chunk with several markers in it. If we
+;; receive a chunk of text which looks like it might contain the
+;; beginning of a marker, we save it here between calls to the
+;; filter.
+(defvar gud-rubydb-marker-acc "")
+(make-variable-buffer-local 'gud-rubydb-marker-acc)
+
+(defun gud-rubydb-marker-filter (string)
+ (setq gud-rubydb-marker-acc (concat gud-rubydb-marker-acc string))
+ (let ((output ""))
+
+ ;; Process all the complete markers in this chunk.
+ (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n"
+ gud-rubydb-marker-acc)
+ (setq
+
+ ;; Extract the frame position from the marker.
+ gud-last-frame
+ (cons (substring gud-rubydb-marker-acc (match-beginning 1) (match-end 1))
+ (string-to-int (substring gud-rubydb-marker-acc
+ (match-beginning 2)
+ (match-end 2))))
+
+ ;; Append any text before the marker to the output we're going
+ ;; to return - we don't include the marker in this text.
+ output (concat output
+ (substring gud-rubydb-marker-acc 0 (match-beginning 0)))
+
+ ;; Set the accumulator to the remaining text.
+ gud-rubydb-marker-acc (substring gud-rubydb-marker-acc (match-end 0))))
+
+ ;; Does the remaining text look like it might end with the
+ ;; beginning of another marker? If it does, then keep it in
+ ;; gud-rubydb-marker-acc until we receive the rest of it. Since we
+ ;; know the full marker regexp above failed, it's pretty simple to
+ ;; test for marker starts.
+ (if (string-match "\032.*\\'" gud-rubydb-marker-acc)
+ (progn
+ ;; Everything before the potential marker start can be output.
+ (setq output (concat output (substring gud-rubydb-marker-acc
+ 0 (match-beginning 0))))
+
+ ;; Everything after, we save, to combine with later input.
+ (setq gud-rubydb-marker-acc
+ (substring gud-rubydb-marker-acc (match-beginning 0))))
+
+ (setq output (concat output gud-rubydb-marker-acc)
+ gud-rubydb-marker-acc ""))
+
+ output))
+
+(defun gud-rubydb-find-file (f)
+ (save-excursion
+ (let ((buf (find-file-noselect f)))
+ (set-buffer buf)
+;; (gud-make-debug-menu)
+ buf)))
+
+(defvar rubydb-command-name "ruby"
+ "File name for executing ruby.")
+
+;;;###autoload
+(defun rubydb (command-line)
+ "Run rubydb on program FILE in buffer *gud-FILE*.
+The directory containing FILE becomes the initial working directory
+and source-file directory for your debugger."
+ (interactive
+ (list (read-from-minibuffer "Run rubydb (like this): "
+ (if (consp gud-rubydb-history)
+ (car gud-rubydb-history)
+ (concat rubydb-command-name " "))
+ nil nil
+ '(gud-rubydb-history . 1))))
+
+ (if (not (fboundp 'gud-overload-functions))
+ (gud-common-init command-line 'gud-rubydb-massage-args
+ 'gud-rubydb-marker-filter 'gud-rubydb-find-file)
+ (gud-overload-functions '((gud-massage-args . gud-rubydb-massage-args)
+ (gud-marker-filter . gud-rubydb-marker-filter)
+ (gud-find-file . gud-rubydb-find-file)))
+ (gud-common-init command-line rubydb-command-name))
+
+ (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.")
+; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
+ (gud-def gud-step "s" "\C-s" "Step one source line with display.")
+ (gud-def gud-next "n" "\C-n" "Step one line (skip functions).")
+ (gud-def gud-cont "c" "\C-r" "Continue with display.")
+ (gud-def gud-finish "finish" "\C-f" "Finish executing current function.")
+ (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).")
+ (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).")
+ (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.")
+
+ (setq comint-prompt-regexp "^(rdb:-) ")
+ (if (boundp 'comint-last-output-start)
+ (set-marker comint-last-output-start (point)))
+ (set (make-local-variable 'paragraph-start) comint-prompt-regexp)
+ (run-hooks 'rubydb-mode-hook)
+ )
diff --git a/misc/test_lldb_cruby.rb b/misc/test_lldb_cruby.rb
index bd58619ac2..4d1cc499f5 100644
--- a/misc/test_lldb_cruby.rb
+++ b/misc/test_lldb_cruby.rb
@@ -9,7 +9,7 @@ class TestLLDBInit < Test::Unit::TestCase
tf.puts <<eom
target create ./miniruby
command script import -r misc/lldb_cruby.py
-b rb_inspect
+b rb_p
run -e'p #{expr}'
rp obj
eom
@@ -24,12 +24,8 @@ eom
assert_rp 'Object.new', 'T_OBJECT'
end
- def test_rp_regex
- assert_rp '/foo/', '[(]Regex'
- end
-
def test_rp_symbol
- assert_rp ':abcde', /T_SYMBOL: \(\h+\)/
+ assert_rp ':abcde', /immediate\(\h+\)/
end
def test_rp_string
diff --git a/misc/yjit_perf.py b/misc/yjit_perf.py
deleted file mode 100755
index 61434e5eb4..0000000000
--- a/misc/yjit_perf.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python3
-import os
-import sys
-from collections import Counter, defaultdict
-import os.path
-
-# Aggregating cycles per symbol and dso
-total_cycles = 0
-category_cycles = Counter()
-detailed_category_cycles = defaultdict(Counter)
-categories = set()
-
-def truncate_symbol(symbol, max_length=50):
- """ Truncate the symbol name to a maximum length """
- return symbol if len(symbol) <= max_length else symbol[:max_length-3] + '...'
-
-def categorize_symbol(dso, symbol):
- """ Categorize the symbol based on the defined criteria """
- if dso == 'sqlite3_native.so':
- return '[sqlite3]'
- elif 'SHA256' in symbol:
- return '[sha256]'
- elif symbol.startswith('[JIT] gen_send'):
- return '[JIT send]'
- elif symbol.startswith('[JIT]'):
- return '[JIT code]'
- elif '::' in symbol or symbol.startswith('yjit::') or symbol.startswith('_ZN4yjit'):
- return '[YJIT compile]'
- elif symbol.startswith('rb_vm_') or symbol.startswith('vm_') or symbol in {
- "rb_call0", "callable_method_entry_or_negative", "invoke_block_from_c_bh",
- "rb_funcallv_scope", "setup_parameters_complex", "rb_yield"}:
- return '[interpreter]'
- elif symbol.startswith('rb_hash_') or symbol.startswith('hash_'):
- return '[rb_hash_*]'
- elif symbol.startswith('rb_ary_') or symbol.startswith('ary_'):
- return '[rb_ary_*]'
- elif symbol.startswith('rb_str_') or symbol.startswith('str_'):
- return '[rb_str_*]'
- elif symbol.startswith('rb_sym') or symbol.startswith('sym_'):
- return '[rb_sym_*]'
- elif symbol.startswith('rb_st_') or symbol.startswith('st_'):
- return '[rb_st_*]'
- elif symbol.startswith('rb_ivar_') or 'shape' in symbol:
- return '[ivars]'
- elif 'match' in symbol or symbol.startswith('rb_reg') or symbol.startswith('onig'):
- return '[regexp]'
- elif 'alloc' in symbol or 'free' in symbol or 'gc' in symbol:
- return '[GC]'
- elif 'pthread' in symbol and 'lock' in symbol:
- return '[pthread lock]'
- else:
- return symbol # Return the symbol itself for uncategorized symbols
-
-def process_event(event):
- global total_cycles, category_cycles, detailed_category_cycles, categories
-
- full_dso = event.get("dso", "Unknown_dso")
- dso = os.path.basename(full_dso)
- symbol = event.get("symbol", "[unknown]")
- cycles = event["sample"]["period"]
- total_cycles += cycles
-
- category = categorize_symbol(dso, symbol)
- category_cycles[category] += cycles
- detailed_category_cycles[category][(dso, symbol)] += cycles
-
- if category.startswith('[') and category.endswith(']'):
- categories.add(category)
-
-def trace_end():
- if total_cycles == 0:
- return
-
- print("Aggregated Event Data:")
- print("{:<20} {:<50} {:>20} {:>15}".format("[dso]", "[symbol or category]", "[top-most cycle ratio]", "[num cycles]"))
-
- for category, cycles in category_cycles.most_common():
- ratio = (cycles / total_cycles) * 100
- dsos = {dso for dso, _ in detailed_category_cycles[category]}
- dso_display = next(iter(dsos)) if len(dsos) == 1 else "Multiple DSOs"
- print("{:<20} {:<50} {:>20.2f}% {:>15}".format(dso_display, truncate_symbol(category), ratio, cycles))
-
- # Category breakdown
- for category in categories:
- symbols = detailed_category_cycles[category]
- category_total = sum(symbols.values())
- category_ratio = (category_total / total_cycles) * 100
- print(f"\nCategory: {category} ({category_ratio:.2f}%)")
- print("{:<20} {:<50} {:>20} {:>15}".format("[dso]", "[symbol]", "[top-most cycle ratio]", "[num cycles]"))
- for (dso, symbol), cycles in symbols.most_common():
- symbol_ratio = (cycles / category_total) * 100
- print("{:<20} {:<50} {:>20.2f}% {:>15}".format(dso, truncate_symbol(symbol), symbol_ratio, cycles))
-
-# There are two ways to use this script:
-# 1) perf script -s misc/yjit_perf.py -- native interface
-# 2) perf script > perf.txt && misc/yjit_perf.py perf.txt -- hack, which doesn't require perf with Python support
-#
-# In both cases, __name__ is "__main__". The following code implements (2) when sys.argv is 2.
-if __name__ == "__main__" and len(sys.argv) == 2:
- if len(sys.argv) != 2:
- print("Usage: yjit_perf.py <filename>")
- sys.exit(1)
-
- with open(sys.argv[1], "r") as file:
- for line in file:
- # [Example]
- # ruby 78207 3482.848465: 1212775 cpu_core/cycles:P/: 5c0333f682e1 [JIT] getlocal_WC_0+0x0 (/tmp/perf-78207.map)
- row = line.split(maxsplit=6)
-
- period = row[3] # "1212775"
- symbol, dso = row[6].split(" (") # "[JIT] getlocal_WC_0+0x0", "/tmp/perf-78207.map)\n"
- symbol = symbol.split("+")[0] # "[JIT] getlocal_WC_0"
- dso = dso.split(")")[0] # "/tmp/perf-78207.map"
-
- process_event({"dso": dso, "symbol": symbol, "sample": {"period": int(period)}})
- trace_end()