Module: Police::VmInfo

Defined in:
lib/police/vminfo/paths.rb,
lib/police/vminfo/objects.rb,
lib/police/vminfo/signature.rb

Defined Under Namespace

Modules: Paths

Class Method Summary collapse

Class Method Details

._kernel_backtick(command) ⇒ String

Executes Kernel.‘ without external interferences.

This is meant for internal use only.

Parameters:

  • command (String)

    the command to be executed

Returns:

  • (String)

    the command’s stdout



216
217
218
219
220
221
222
223
224
# File 'lib/police/vminfo/objects.rb', line 216

def self._kernel_backtick(command)
  if defined?(Bundler)
    Bundler.with_clean_env do
      Kernel.send :`, command
    end
  else
    Kernel.send :`, command
  end
end

.all_classesArray<Classes>

All loaded Ruby classes, obtained by querying ObjectSpace.

Querying ObjectSpace can be painfully slow, especially on non-MRI VMs. Note that all classes are modules, so this is a subset of all_modules.

Returns:

  • (Array<Classes>)

    all the Ruby classes



60
61
62
# File 'lib/police/vminfo/objects.rb', line 60

def self.all_classes
  ObjectSpace.each_object(Class).to_a
end

.all_methods(module_or_class) ⇒ Array<UnboundMethod>

All methods defined in a class or module.

Parameters:

  • module_or_class (Module)

    a Class or Module instance

Returns:

  • (Array<UnboundMethod>)

    all the class and instance methods defined by the given Class or Module



109
110
111
# File 'lib/police/vminfo/objects.rb', line 109

def self.all_methods(module_or_class)
  class_methods(module_or_class) + instance_methods(module_or_class)
end

.all_modulesArray<Module>

All loaded Ruby modules, obtained by querying ObjectSpace.

Querying ObjectSpace can be painfully slow, especially on non-MRI VMs.

Returns:

  • (Array<Module>)

    all the Ruby modules



50
51
52
# File 'lib/police/vminfo/objects.rb', line 50

def self.all_modules
  ObjectSpace.each_object(Module).to_a
end

.class_methods(module_or_class) ⇒ Array<UnboundMethod>

All class methods defined in a class or module.

Note: the class methods of a class or module are the instance methods of the class or module’s meta-class.

Parameters:

  • module_or_class (Module)

    a Class or Module instance

Returns:

  • (Array<UnboundMethod>)

    all the instance methods defined by the given Class or Module



133
134
135
136
137
138
139
140
141
142
# File 'lib/police/vminfo/objects.rb', line 133

def self.class_methods(module_or_class)
  # NOTE: this long-winded approach avoids creating new singleton classes
  method_names = module_or_class.singleton_methods
  return [] if method_names.empty?
  singleton_class = module_or_class.singleton_class
  method_names.tap do |array|
    array.map! { |name| singleton_class.instance_method name }
    array.select! { |method| method.owner == singleton_class }
  end
end

.constantize(name) ⇒ Object

Resolves the name of a constant into its value.

Parameters:

  • name (String)

    a constant name, potentially including the scope

    operator

Returns:

  • (Object)

    the value of the constant with the given name

Raises:

  • (NameError)

    no constant with the given name is defined



195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/police/vminfo/objects.rb', line 195

def self.constantize(name)
  segments = name.split '::'
  value = Object
  segments.each do |segment|
    next if segment.empty?
    value = if value.const_defined? segment
      value.const_get segment
    else
      value.const_missing segment
    end
  end
  value
end

.core_class_methods(module_or_class) ⇒ Array<UnboundMethod>

The core class methods defined in a core class or module.

Parameters:

  • module_or_class (Module)

    the module or class whose class methods will be retrieved; should be one of the core modules / classes in the Ruby VM

Returns:

  • (Array<UnboundMethod>)

    the class methods defined by the Ruby VM



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/police/vminfo/objects.rb', line 171

def self.core_class_methods(module_or_class)
  ruby = Gem.ruby
  output = _kernel_backtick(
      %Q|#{ruby} -e 'puts #{module_or_class}.singleton_methods.join("\n")'|)

  methods = []
  method_names = output.split "\n"
  return [] if method_names.empty?
  singleton_class = module_or_class.singleton_class
  output.split("\n").each do |name|
    method = singleton_class.instance_method name.to_sym
    next unless method.owner == singleton_class
    # TODO(pwnall): consider checking for re-defined core methods
    methods << method
  end
  methods
end

.core_classesArray<Class>

The classes making up the Ruby VM implementation.

Note that all classes are modules, so this is a subset of core_modules.

Returns:

  • (Array<Class>)

    the classes that are present in a vanilla Ruby environment



97
98
99
100
101
# File 'lib/police/vminfo/objects.rb', line 97

def self.core_classes
  return @core_classes if @core_classes
  @core_classes = core_modules.select { |m| m.kind_of? Class }
  @core_classes.freeze
end

.core_instance_methods(module_or_class) ⇒ Array<UnboundMethod>

The core instance methods defined in a core class or module.

Parameters:

  • module_or_class (Module)

    the module or class whose instance methods will be retrieved; should be one of the core modules / classes in the Ruby VM

Returns:

  • (Array<UnboundMethod>)

    the instance methods defined by the Ruby VM



150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/police/vminfo/objects.rb', line 150

def self.core_instance_methods(module_or_class)
  ruby = Gem.ruby
  output = _kernel_backtick(
      %Q|#{ruby} -e 'puts #{module_or_class}.instance_methods.join("\n")'|)

  methods = []
  output.split("\n").each do |name|
    method = module_or_class.instance_method name.to_sym
    next unless method.owner == module_or_class
    # TODO(pwnall): consider checking for re-defined core methods
    methods << method
  end
  methods
end

.core_modulesArray<Module>

The modules making up the Ruby VM implementation.

Returns:

  • (Array<Module>)

    the modules that are present in a vanilla Ruby environment



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/police/vminfo/objects.rb', line 68

def self.core_modules
  return @core_modules if @core_modules

  ruby = Gem.ruby
  output = _kernel_backtick(
      %Q|#{ruby} -e 'puts ObjectSpace.each_object(Module).to_a.join("\n")'|)
  modules = []
  output.split("\n").each do |name|
    next if name[0] == ?#
    begin
      mod = constantize name
      next unless mod.kind_of? Module
      modules << mod
    rescue NameError, LoadError
      # Delayed loading failure.
      next
    end
  end

  @core_modules = modules.freeze
end

.gem_path?(path) ⇒ Boolean

True if the given source code path belongs to a gem.

Returns:

  • (Boolean)


25
26
27
# File 'lib/police/vminfo/paths.rb', line 25

def self.gem_path?(path)
  Gem.default_path.any? { |gem_path| Paths.descendant? path, gem_path }
end

.instance_methods(module_or_class) ⇒ Array<UnboundMethod>

All instance methods defined in a class or module.

Parameters:

  • module_or_class (Module)

    a Class or Module instance

Returns:

  • (Array<UnboundMethod>)

    all the instance methods defined by the given Class or Module



118
119
120
121
122
123
# File 'lib/police/vminfo/objects.rb', line 118

def self.instance_methods(module_or_class)
  module_or_class.instance_methods.tap do |array|
    array.map! { |name| module_or_class.instance_method name }
    array.select! { |method| method.owner == module_or_class }
  end
end

.kernel_path?(path) ⇒ Boolean

True if the given source code path belongs to the Ruby VM kernel.

Returns:

  • (Boolean)


46
47
48
# File 'lib/police/vminfo/paths.rb', line 46

def self.kernel_path?(path)
  !$LOAD_PATH.any? { |load_path| Paths.descendant? path, load_path }
end

.method_source(method) ⇒ Symbol

Classifies the path to a Ruby file based on its provenance.

Parameters:

  • method (Method, UnboundMethod)

    VM information about a method; usually obtained by calling Object#method or Module#instance_method

Returns:

  • (Symbol)

    :native for methods defined in a low-level language (C/C++ for MRI and Rubinius, Java for JRuby), :kernel for methods belonging to the core VM code (Rubinius and JRuby), :stdlib for methods in Ruby’s standard library, :gem for methods that are implemented in a loaded Ruby gem, and :app for all other methods (presumably defined in the current application)



14
15
16
17
18
19
20
21
22
# File 'lib/police/vminfo/paths.rb', line 14

def self.method_source(method)
  location = method.source_location
  return :native if location.nil?
  code_path = location.first
  return :stdlib if stdlib_path?(code_path)
  return :gem if gem_path?(code_path)
  return :kernel if kernel_path?(code_path)
  :app
end

.named_classesArray<Module>

All loaded Ruby classes, obtained by walking the constants graph.

Note that all classes are modules, so this is a subset of named_modules.

Returns:

  • (Array<Module>)

    the Ruby classes that could be discovered by searching the constants graph; this should include everything except for anonymous (not assigned to constants) classes



41
42
43
# File 'lib/police/vminfo/objects.rb', line 41

def self.named_classes
  named_modules.select { |m| m.kind_of? Class }
end

.named_modulesArray<Module>

All loaded Ruby modules, obtained by walking the constants graph.

Returns:

  • (Array<Module>)

    the Ruby modules that could be discovered by searching the constants graph; this should include everything except for anonymous (not assigned to constants) classes



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/police/vminfo/objects.rb', line 9

def self.named_modules
  # NOTE: this is a Set, but we don't want to load the module.
  explored = { Object => true }
  left = [Object]
  until left.empty?
    namespace = left.pop
    namespace.constants.each do |const_name|
      begin
        const = if namespace.const_defined? const_name
          namespace.const_get const_name
        else
          namespace.const_missing const_name
        end
      rescue LoadError, NameError
        # Delayed loading failure.
        next
      end
      next if explored[const] || !const.kind_of?(Module)
      explored[const] = true
      left.push const
    end
  end
  explored.keys.sort_by!(&:name).freeze
end

.signatureString

Fingerprint for the Ruby VM’s core and stdlib API.

Returns:

  • (String)

    a string that contains the Ruby VM’s engine name and core version; this should be representative of the



8
9
10
# File 'lib/police/vminfo/signature.rb', line 8

def self.signature
  Gem.ruby_engine + Gem.ruby_version.segments[0, 3].join('.')
end

.stdlib_path?(path) ⇒ Boolean

True if the given source code path belongs to the Ruby standard library.

Returns:

  • (Boolean)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/police/vminfo/paths.rb', line 30

def self.stdlib_path?(path)
  # NOTE: assuming the convention that all directories are prepended to the
  #       load path throughout a program's execution
  load_paths = $LOAD_PATH
  last_gem_index = -1
  (load_paths.length - 1).downto(0) do |index|
    if gem_path? load_paths[index]
      last_gem_index = index
      break
    end
  end
  stdlib_paths = load_paths[(last_gem_index + 1)..-1]
  stdlib_paths.any? { |stdlib_path| Paths.descendant? path, stdlib_path }
end