module Police::VmInfo

Public Class Methods

_kernel_backtick(command) click to toggle source

Executes Kernel.‘ without external interferences.

@private This is meant for internal use only.

@param {String} command the command to be executed @return {String} the command’s stdout

# 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_classes() click to toggle source

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.

@return [Array<Classes>] all the Ruby classes

# File lib/police/vminfo/objects.rb, line 60
def self.all_classes
  ObjectSpace.each_object(Class).to_a
end
all_methods(module_or_class) click to toggle source

All methods defined in a class or module.

@param [Module] module_or_class a Class or Module instance @return [Array<UnboundMethod>] all the class and instance methods

defined by the given Class or Module
# 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_modules() click to toggle source

All loaded Ruby modules, obtained by querying ObjectSpace.

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

@return [Array<Module>] all the Ruby modules

# File lib/police/vminfo/objects.rb, line 50
def self.all_modules
  ObjectSpace.each_object(Module).to_a
end
class_methods(module_or_class) click to toggle source

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.

@param [Module] module_or_class a Class or Module instance @return [Array<UnboundMethod>] all the instance methods defined by the

given Class or Module
# 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) click to toggle source

Resolves the name of a constant into its value.

@param [String] name a constant name, potentially including the scope

operator ::

@return [Object] the value of the constant with the given name @raise [NameError] no constant with the given name is defined

# 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) click to toggle source

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

@param [Module] module_or_class the module or class whose class methods

will be retrieved; should be one of the core modules / classes in the
Ruby VM

@return [Array<UnboundMethod>] the class methods defined by the Ruby VM

# 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_classes() click to toggle source

The classes making up the Ruby VM implementation.

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

@return [Array<Class>] the classes that are present in a vanilla Ruby

environment
# 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) click to toggle source

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

@param [Module] module_or_class the module or class whose instance methods

will be retrieved; should be one of the core modules / classes in the
Ruby VM

@return [Array<UnboundMethod>] the instance methods defined by the Ruby VM

# 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_modules() click to toggle source

The modules making up the Ruby VM implementation.

@return [Array<Module>] the modules that are present in a vanilla Ruby

environment
# 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) click to toggle source

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

# 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) click to toggle source

All instance methods defined in a class or module.

@param [Module] module_or_class a Class or Module instance @return [Array<UnboundMethod>] all the instance methods defined by the

given Class or Module
# 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) click to toggle source

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

# 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) click to toggle source

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

@param [Method, UnboundMethod] method VM information about a method;

usually obtained by calling Object#method or Module#instance_method

@return [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)
# 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_classes() click to toggle source

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

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

@return [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
# File lib/police/vminfo/objects.rb, line 41
def self.named_classes
  named_modules.select { |m| m.kind_of? Class }
end
named_modules() click to toggle source

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

@return [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
# 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
signature() click to toggle source

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

@return [String] a string that contains the Ruby VM’s engine name and

core version; this should be representative of the
# 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) click to toggle source

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

# 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