summaryrefslogtreecommitdiff
path: root/lib/method_source/source_location.rb
blob: 501052024a05cb3ca58772aec704dd6edc35e0c0 (plain)
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
module MethodSource
  module SourceLocation
    module MethodExtensions
      if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
        require 'java'

        # JRuby version source_location hack
        # @return [Array] A two element array containing the source location of the method
        def source_location
          to_java.source_location(Thread.current.to_java.getContext())
        end
      else


        def trace_func(event, file, line, id, binding, classname)
          return unless event == 'call'
          set_trace_func nil

          @file, @line = file, line
          raise :found
        end

        private :trace_func

        # Return the source location of a method for Ruby 1.8.
        # @return [Array] A two element array. First element is the
        #   file, second element is the line in the file where the
        #   method definition is found.
        def source_location
          if @file.nil?
            args =[*(1..(arity<-1 ? -arity-1 : arity ))]

            set_trace_func method(:trace_func).to_proc
            call(*args) rescue nil
            set_trace_func nil
            @file = File.expand_path(@file) if @file && File.exist?(File.expand_path(@file))
          end
          return [@file, @line] if File.exist?(@file.to_s)
        end
      end
    end

    module ProcExtensions

      if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/

        # Return the source location for a Proc (Rubinius only)
        # @return [Array] A two element array. First element is the
        #   file, second element is the line in the file where the
        #   proc definition is found.
        def source_location
          [block.file.to_s, block.line]
        end

      else

        # Return the source location for a Proc (in implementations
        # without Proc#source_location)
        # @return [Array] A two element array. First element is the
        #   file, second element is the line in the file where the
        #   proc definition is found.
        def source_location
          self.to_s =~ /@(.*):(\d+)/
          [$1, $2.to_i]
        end
      end
    end

    module UnboundMethodExtensions
      if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
        require 'java'

        # JRuby version source_location hack
        # @return [Array] A two element array containing the source location of the method
        def source_location
          to_java.source_location(Thread.current.to_java.getContext())
        end
      else


        # Return the source location of an instance method for Ruby 1.8.
        # @return [Array] A two element array. First element is the
        #   file, second element is the line in the file where the
        #   method definition is found.
        def source_location
          klass = case owner
                  when Class
                    owner
                  when Module
                    method_owner = owner
                    Class.new { include(method_owner) }
                  end

          # deal with immediate values
          case
          when klass == Symbol
            return :a.method(name).source_location
          when klass == Fixnum
            return 0.method(name).source_location
          when klass == TrueClass
            return true.method(name).source_location
          when klass == FalseClass
            return false.method(name).source_location
          when klass == NilClass
            return nil.method(name).source_location
          end

          begin
            klass.allocate.method(name).source_location
          rescue TypeError

            # Assume we are dealing with a Singleton Class:
            # 1. Get the instance object
            # 2. Forward the source_location lookup to the instance
            instance ||= ObjectSpace.each_object(owner).first
            instance.method(name).source_location
          end
        end
      end
    end
  end
end