TOPLEVEL_BINDING = binding()
module Rubinius
class Loader
def initialize
@exit_code = 0
@load_paths = []
@requires = []
@evals = []
@script = nil
@verbose_eval = false
@debugging = false
@run_irb = true
end
# Finish setting up after loading kernel.
def preamble
@stage = "running Loader preamble"
Object.const_set :ENV, EnvironmentVariables.new
String.ruby_parser if ENV['RUBY_PARSER']
String.sydney_parser if ENV['SYDNEY'] or ENV['SYDPARSE']
# define a global "start time" to use for process calculation
$STARTUP_TIME = Time.now
# set terminal width
width = 80
if Terminal and !ENV['RBX_NO_COLS']
begin
`which tput &> /dev/null`
if $?.exitstatus == 0
width = `tput cols`.to_i
end
end
end
Rubinius.const_set 'TERMINAL_WIDTH', width
$VERBOSE = false
end
# Setup $LOAD_PATH.
def system_load_path
@stage = "setting up system load path"
# Add a fallback directory if Rubinius::LIB_PATH doesn't exist
@main_lib = File.expand_path(LIB_PATH)
@main_lib = File.join(Dir.pwd, 'lib') unless File.exists?(@main_lib)
# This conforms more closely to MRI. It is necessary to support
# paths that mkmf adds when compiling and installing native exts.
additions = []
additions << SITELIBDIR
additions << SITEARCHDIR
additions << SITEDIR
additions << RUBYLIBDIR
additions << @main_lib
additions.uniq!
$LOAD_PATH.unshift(*additions)
if ENV['RUBYLIB'] and not ENV['RUBYLIB'].empty? then
rubylib_paths = ENV['RUBYLIB'].split(':')
$LOAD_PATH.unshift(*rubylib_paths)
end
end
# Load customization code:
# /etc/rbxrc
# $HOME/.rbxrc
# $RBX_PRELOAD
def preload
@stage = "preloading rbxrc code"
['/etc/rbxrc',"#{ENV['HOME']}/.rbxrc",ENV['RBX_PRELOAD']].each do |file|
begin
load file if file and File.exist?(file)
rescue LoadError
nil
end
end
end
# Register signal handlers.
def signals
@stage = "registering signal handlers"
# Set up a handler for SIGINT that raises Interrupt on the main thread
Signal.trap("INT") do |sig|
raise Interrupt, "Thread has been interrupted"
end
end
# Process all command line arguments.
def options(argv=ARGV)
@stage = "processing command line arguments"
options = Options.new "Usage: rbx [options] [--] [script] [arguments]", 25
options.left_align
options.on_extra do |x|
raise ParseError, "Unrecognized option: #{x}" if x[0] == ?-
if @script.nil? and @evals.empty?
@script = x
else
ARGV.unshift x
end
options.stop_parsing
end
options.doc "Script is any valid Ruby source file (.rb) or a compiled Ruby file (.rbc)."
options.doc "\nRuby options"
options.on "-", "Read and evaluate code from STDIN" do
@run_irb = false
$0 = "-"
Compiler::Utils.execute STDIN.read
end
options.on "--", "Stop processing command line arguments" do
options.stop_parsing
end
options.on "-C", "DIR", "Change directory to DIR before running scripts" do |dir|
@directory = dir
end
options.on "-d", "Enable debugging output and set $DEBUG to true" do
$DEBUG = true
end
options.on "-e", "CODE", "Compile and execute CODE" do |code|
@run_irb = false
$0 = "(eval)"
@evals << code
end
options.on "-E", "CODE", "Compile and execute CODE (show sexp and bytecode)" do |code|
@run_irb = false
$0 = "(eval)"
@verbose_eval = true
@evals << code
end
options.on "-h", "--help", "Display this help" do
@run_irb = false
puts options
done
end
options.on "-i", "EXT", "Edit ARGV files in place, making backup with EXT" do |ext|
# in place edit mode
$-i = ext
end
options.on "-I", "DIR1[:DIR2]", "Add directories to $LOAD_PATH" do |dir|
@load_paths << dir
end
options.on "-r", "LIBRARY", "Require library before execution" do |file|
@requires << file
end
options.on("-S", "SCRIPT",
"Run SCRIPT using PATH environment variable to find it") do |script|
options.stop_parsing
@run_irb = false
search = ENV['PATH'].split(File::PATH_SEPARATOR).unshift(BIN_PATH)
dir = search.detect do |d|
path = File.join(d, script)
File.exist?(path)
end
file = File.join(dir, script) if dir
$0 = script if file
# if missing, let it die a natural death
@script = file ? file : script
end
options.on "-v", "Display the version and set $VERBOSE to true" do
@run_irb = false
$VERBOSE = true
puts Rubinius.version
end
options.on "-w", "Enable warnings" do
# TODO: implement
end
options.on "--version", "Display the version" do
@run_irb = false
puts Rubinius.version
end
# TODO: convert all these to -X options
options.doc "\nRubinius options"
options.on "--debug", "Launch the debugger" do
require 'debugger/interface'
Debugger::CmdLineInterface.new
@debugging = true
end
options.on "--remote-debug", "Run the program under the control of a remote debugger" do
require 'debugger/debug_server'
if port = (ARGV.first =~ /^\d+$/ and ARGV.shift)
$DEBUG_SERVER = Debugger::Server.new(port.to_i)
else
$DEBUG_SERVER = Debugger::Server.new
end
$DEBUG_SERVER.listen
@debugging = true
end
options.on "--dc", "Display debugging information for the compiler" do
puts "[Compiler debugging enabled]"
$DEBUG_COMPILER = true
end
options.on "--dl", "Display debugging information for the loader" do
$DEBUG_LOADING = true
puts "[Code loading debugging enabled]"
end
options.on "--fast", "Compile specific methods at startup" do
Rubinius.compile_common_methods
end
options.on "--gc-stats", "Show GC stats" do
stats = Stats::GC.new
at_exit { stats.show }
end
options.on("-P", "[COLUMN]",
"Run the profiler, optionally sort output by COLUMN") do |columns|
require 'profile'
if columns
Profiler__.options :sort => columns.split(/,/).map {|x| x.to_sym }
end
end
options.on "--ruby_parser", "Use RubyParser" do
String.ruby_parser
end
options.on "--sydney", "Use SydneyParser" do
String.sydney_parser
end
options.on "--vv", "Display version and extra info" do
@run_irb = false
$VERBOSE = true
puts Rubinius.version
puts "Options:"
puts " Interpreter type: #{INTERPRETER}"
if jit = JIT
puts " JIT enabled: #{jit}"
else
puts " JIT disabled"
end
puts
end
options.parse ARGV
end
# Update the load paths with any -I arguments.
def load_paths
@stage = "setting load paths"
@load_paths.each do |path|
path.split(":").reverse_each do |path|
path = File.expand_path path
$LOAD_PATH.unshift(path)
end
end
end
# Require any -r arguments
def requires
@stage = "requiring command line files"
@requires.each { |file| require file }
end
# Evaluate any -e arguments
def evals
@stage = "evaluating command line code"
@evals.each do |code|
eval(code, TOPLEVEL_BINDING) do |compiled_method|
if @verbose_eval
p code.to_sexp("(eval)", 1)
puts compiled_method.decode
end
end
end
rescue SystemExit => e
@exit_code = e.status
end
# Run all scripts passed on the command line
def script
return unless @script and @evals.empty?
@stage = "running #{@script}"
Dir.chdir @directory if @directory
if File.exist?(@script)
$0 = @script
Compiler::Utils.debug_script! if @debugging
Compiler::Utils.load_from_extension @script
else
if @script.suffix?(".rb")
puts "Unable to find '#{@script}'"
exit! 1
else
prog = File.join @main_lib, "bin", "#{@script}.rb"
if File.exist? prog
$0 = prog
load prog
else
raise LoadError, "Unable to find a script '#{@script}' to run"
end
end
end
rescue SystemExit => e
@exit_code = e.status
end
# Run IRB unless we were passed -e, -S arguments or a script to run.
def irb
return if $0 or not @run_irb
@stage = "running IRB"
if Terminal
repr = ENV['RBX_REPR'] || "bin/irb"
$0 = repr
prog = File.join @main_lib, repr
begin
# HACK: this was load but load raises LoadError
# with prog == "lib/bin/irb". However, require works.
# Investigate when we have specs running.
require prog
rescue LoadError => e
STDERR.puts "Unable to find repr named '#{repr}' to load."
puts e.awesome_backtrace.show
exit 1
end
else
$0 = "(eval)"
Compiler::Utils.execute "p #{STDIN.read}"
end
end
# Cleanup and at_exit processing.
def epilogue
@stage = "at_exit handler"
AtExit.shift.call until AtExit.empty?
@stage = "object finalizers"
ObjectSpace.run_finalizers
# TODO: Fix these with better -X processing
if RUBY_CONFIG['rbx.jit_stats']
stats = VM.jit_info
puts "JIT time spent: #{stats[0] / 1000000}ms"
puts " JITed methods: #{stats[1]}"
end
if RUBY_CONFIG['rbx.gc_stats']
Stats::GC.new.show
end
end
# Exit.
def done
Process.exit @exit_code
end
# Orchestrate everything.
def main
preamble
system_load_path
preload
signals
options
load_paths
requires
evals
script
irb
epilogue
rescue SystemExit => e
@exit_code = e.status
rescue SyntaxError => e
puts "A syntax error has occured:"
puts " #{e.message}"
puts " near line #{e.file}:#{e.line}, column #{e.column}"
puts "\nCode:\n#{e.code}"
if e.column
puts((" " * (e.column - 1)) + "^")
end
puts "\nBacktrace:"
puts e.awesome_backtrace.show
@exit_code = 1
rescue Object => e
begin
if e.kind_of? Exception
msg = e.message
else
msg = "strange object detected as exception: #{e.inspect}"
end
puts "An exception occurred #{@stage}"
puts " #{e.message} (#{e.class})"
puts "\nBacktrace:"
puts e.awesome_backtrace.show
@exit_code = 1
rescue Object => e2
puts "\n====================================="
puts "Exception occurred during top-level exception output! (THIS IS BAD)"
puts
puts "Original Exception: #{e.inspect} (#{e.class})"
puts "New Exception: #{e2.inspect} (#{e.class})"
@exit_code = 128
end
ensure
done
end
end
end
Rubinius::Loader.new.main