public
Description: Rubinius, the Ruby VM
Homepage: http://rubini.us
Clone URL: git://github.com/evanphx/rubinius.git
dgtized (author)
Thu Jul 09 11:49:07 -0700 2009
commit  a0bd2026fb8c8039a7efcfa5fc159bf7eca299d8
tree    e363b0269bc7c5f54dd272d8a4260c011f0d420f
parent  046e04f2008c4de85c9f7d57be5f40d11db072a4
rubinius / kernel / loader.rb
100644 446 lines (369 sloc) 11.701 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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