Class: Capybara::Simulated::StackResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/capybara/simulated/stack_resolver.rb

Overview

Annotates a V8 stack-trace string with original-source positions from each frame's .map companion file. Resolves once per frame, caches loaded Sourcemap objects process-wide (built bundles are fingerprinted; identical URL → identical map).

Frames whose URL has no .map (snapshot stubs, eval'd app inline scripts) pass through unchanged.

Constant Summary collapse

FRAME_RE =

Matches (URL:LINE:COL) inside V8 stack frames. URLs can contain : (e.g. :port), so the URL class includes : and the engine backtracks to find the trailing :digits:digits). file: and csim-eval: schemes are matched too because both appear in snapshot-warmup frames where the host wraps eval'd content.

/\((?<url>(?:https?:\/\/|file:\/\/|csim-eval:)[^\s()]+):(?<line>\d+):(?<col>\d+)\)/
@@maps =
{}
@@lock =
Mutex.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(browser) ⇒ StackResolver

Returns a new instance of StackResolver.



33
34
35
# File 'lib/capybara/simulated/stack_resolver.rb', line 33

def initialize(browser)
  @browser = browser
end

Class Method Details

.clearObject

Drop the fetched source maps (part of Browser.clear_http_cache).



29
30
31
# File 'lib/capybara/simulated/stack_resolver.rb', line 29

def self.clear
  @@lock.synchronize { @@maps.clear }
end

Instance Method Details

#annotate(text) ⇒ Object

Returns the input stack/text with → source:line:col appended to every frame whose URL maps to a .map. If no frame resolves, the input is returned unchanged.



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/capybara/simulated/stack_resolver.rb', line 40

def annotate(text)
  return text unless text.is_a?(String) && !text.empty?
  text.gsub(FRAME_RE) {
    m = ::Regexp.last_match
    url  = m[:url]
    line = m[:line].to_i
    col  = m[:col].to_i
    resolved = resolve(url, line, col)
    resolved ? "#{m[0]}#{resolved}" : m[0]
  }
end