class Sentry::Backtrace

@api private

def self.line_cache

def self.line_cache
  @line_cache ||= Concurrent::Map.new
end

def self.parse(backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback)

def self.parse(backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback)
  ruby_lines = backtrace.is_a?(Array) ? backtrace : backtrace.split(/\n\s*/)
  ruby_lines = backtrace_cleanup_callback.call(ruby_lines) if backtrace_cleanup_callback
  in_app_pattern ||= begin
    Regexp.new("^(#{project_root}/)?#{app_dirs_pattern}")
  end
  lines = ruby_lines.to_a.map do |unparsed_line|
    Line.parse(unparsed_line, in_app_pattern)
  end
  new(lines)
end

def self.source_location(&backtrace_cleaner)

def self.source_location(&backtrace_cleaner)
  Thread.each_caller_location do |location|
    frame_key = [location.absolute_path, location.lineno]
    cached_value = line_cache[frame_key]
    next if cached_value == :skip
    if cached_value
      return cached_value
    else
      if cleaned_frame = backtrace_cleaner.(location)
        line = Line.from_source_location(location)
        line_cache[frame_key] = line
        return line
      else
        line_cache[frame_key] = :skip
        next
      end
    end
  end
end

def self.source_location(*)

application.
to the slower implementation and adds potentially big overhead to the
Since Sentry is mostly used in production, we don't want to fallback
def self.source_location(*)
  nil
end

def ==(other)

def ==(other)
  if other.respond_to?(:lines)
    lines == other.lines
  else
    false
  end
end

def initialize(lines)

def initialize(lines)
  @lines = lines
end

def inspect

def inspect
  "<Backtrace: " + lines.map(&:inspect).join(", ") + ">"
end

def to_s

def to_s
  content = []
  lines.each do |line|
    content << line
  end
  content.join("\n")
end