module Byebug::DAP::ValueHelpers

Methods to prepare values for DAP responses. @api private

Public Instance Methods

prepare_value(val) { || ... } click to toggle source

Safely inspect a value and retrieve its class name, class and instance variables, and indexed members. {Scalar} values do not have variables or members. Only {std:Array arrays} and {std:Hash hashes} have members. @return `val.inspect`, `val.class.name`, variables, and members.

# File lib/byebug/dap/helpers/value_helpers.rb, line 9
def prepare_value(val)
  str = safe(val, :inspect) { safe(val, :to_s) { return yield } }
  cls = safe(val, :class) { nil }
  typ = safe(cls, :name) { safe(cls, :to_s) { nil } }

  scalar = safe(-> { Scalar === val }, :call) { true }
  return str, typ, [], [] if scalar

  named = safe(val, :instance_variables) { [] } || []
  named += safe(val, :class_variables) { [] } || []
  # named += safe(val, :constants) { [] }

  indexed = safe(-> {
    return (0...val.size).to_a if val.is_a?(Array)
    return val.keys if val.respond_to?(:keys) && val.respond_to?(:[])
    []
  }, :call) { [] }

  return str, typ, named, indexed
end
prepare_value_response(thnum, frnum, kind, name: nil, &block) click to toggle source

Prepare a {Protocol::Variable} or {Protocol::EvaluateResponseBody} for a calculated value. For global variables and evaluations, `thnum` and `frnum` should be 0. Local variables and evaluations are {Command#execute_on_thread executed on the specified thread}. @param thnum [std:Integer] the thread number @param frnum [std:Integer] the frame number @param kind [std:Symbol] `:variable` or `:evaluate` @param name [std:String] the variable name (ignored for evaluations) @yield retrieves an variable or evaluates an expression

# File lib/byebug/dap/helpers/value_helpers.rb, line 39
def prepare_value_response(thnum, frnum, kind, name: nil, &block)
  err = nil
  raw = execute_on_thread(thnum, block) { |e| err = e; nil }

  if err.nil?
    value, type, named, indexed = prepare_value(raw) { |e| next exception_description(e), nil, [], [] }
  else
    type, named, indexed = nil, [], []
    if err.is_a?(CommandProcessor::TimeoutError)
      name = err.context.thread.name
      value = "*Thread ##{err.context.thnum} #{name ? '(' + name + ')' : ''} unresponsive*"
    else
      value = exception_description(err)
    end
  end

  case kind
  when :variable
    klazz = Protocol::Variable
    args = { name: safe(name, :to_s) { safe(name, :inspect) { '???' } }, value: value, type: type }
  when :evaluate
    klazz = Protocol::EvaluateResponseBody
    args = { result: value, type: type }
  end

  if named.empty? && indexed.empty?
    args[:variablesReference] = 0
  else
    args[:variablesReference] = @session.save_variables(thnum, frnum, kind, raw, named, indexed)
    args[:namedVariables] = named.size
    args[:indexedVariables] = indexed.size
  end

  klazz.new(args).validate!
end