Class: Bootsnap::LoadPathCache::Cache

Inherits:
Object
  • Object
show all
Defined in:
lib/bootsnap/load_path_cache/cache.rb

Constant Summary collapse

AGE_THRESHOLD =

seconds

30
TRUFFLERUBY_LIB_DIR_PREFIX =
if RUBY_ENGINE == "truffleruby"
  "#{File.join(RbConfig::CONFIG['libdir'], 'truffle')}#{File::SEPARATOR}"
end
BUILTIN_FEATURES =

{ ‘enumerator’ => nil, ‘enumerator.so’ => nil, … }

$LOADED_FEATURES.each_with_object({}) do |feat, features|
  if TRUFFLERUBY_LIB_DIR_PREFIX && feat.start_with?(TRUFFLERUBY_LIB_DIR_PREFIX)
    feat = feat.byteslice(TRUFFLERUBY_LIB_DIR_PREFIX.bytesize..-1)
  end

  # Builtin features are of the form 'enumerator.so'.
  # All others include paths.
  next unless feat.size < 20 && !feat.include?("/")

  base = File.basename(feat, ".*") # enumerator.so -> enumerator
  ext  = File.extname(feat) # .so

  features[feat] = nil # enumerator.so
  features[base] = nil # enumerator

  next unless [DOT_SO, *DL_EXTENSIONS].include?(ext)

  DL_EXTENSIONS.each do |dl_ext|
    features["#{base}#{dl_ext}"] = nil # enumerator.bundle
  end
end.freeze

Instance Method Summary collapse

Constructor Details

#initialize(store, path_obj, development_mode: false) ⇒ Cache

Returns a new instance of Cache.



10
11
12
13
14
15
16
17
# File 'lib/bootsnap/load_path_cache/cache.rb', line 10

def initialize(store, path_obj, development_mode: false)
  @development_mode = development_mode
  @store = store
  @mutex = Mutex.new
  @path_obj = path_obj.map! { |f| PathScanner.os_path(File.exist?(f) ? File.realpath(f) : f.dup) }
  @has_relative_paths = nil
  reinitialize
end

Instance Method Details

#find(feature) ⇒ Object

Try to resolve this feature to an absolute path without traversing the loadpath.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/bootsnap/load_path_cache/cache.rb', line 56

def find(feature)
  reinitialize if (@has_relative_paths && dir_changed?) || stale?
  feature = feature.to_s.freeze

  return feature if Bootsnap.absolute_path?(feature)

  if feature.start_with?("./", "../")
    return expand_path(feature)
  end

  @mutex.synchronize do
    x = search_index(feature)
    return x if x

    # Ruby has some built-in features that require lies about.
    # For example, 'enumerator' is built in. If you require it, ruby
    # returns false as if it were already loaded; however, there is no
    # file to find on disk. We've pre-built a list of these, and we
    # return false if any of them is loaded.
    return false if BUILTIN_FEATURES.key?(feature)

    # The feature wasn't found on our preliminary search through the index.
    # We resolve this differently depending on what the extension was.
    case File.extname(feature)
    # If the extension was one of the ones we explicitly cache (.rb and the
    # native dynamic extension, e.g. .bundle or .so), we know it was a
    # failure and there's nothing more we can do to find the file.
    # no extension, .rb, (.bundle or .so)
    when "", *CACHED_EXTENSIONS
      nil
    # Ruby allows specifying native extensions as '.so' even when DLEXT
    # is '.bundle'. This is where we handle that case.
    when DOT_SO
      x = search_index(feature[0..-4] + DLEXT)
      return x if x

      if DLEXT2
        x = search_index(feature[0..-4] + DLEXT2)
        return x if x
      end
    else
      # other, unknown extension. For example, `.rake`. Since we haven't
      # cached these, we legitimately need to run the load path search.
      return FALLBACK_SCAN
    end
  end

  # In development mode, we don't want to confidently return failures for
  # cases where the file doesn't appear to be on the load path. We should
  # be able to detect newly-created files without rebooting the
  # application.
  return FALLBACK_SCAN if @development_mode
end

#load_dir(dir) ⇒ Object

What is the path item that contains the dir as child? e.g. given “/a/b/c/d” exists, and the path is [“/a/b”], load_dir(“c/d”) is “/a/b”.



22
23
24
25
# File 'lib/bootsnap/load_path_cache/cache.rb', line 22

def load_dir(dir)
  reinitialize if stale?
  @mutex.synchronize { @dirs[dir] }
end

#push_paths(sender, *paths) ⇒ Object



116
117
118
119
120
# File 'lib/bootsnap/load_path_cache/cache.rb', line 116

def push_paths(sender, *paths)
  return unless sender == @path_obj

  @mutex.synchronize { push_paths_locked(*paths) }
end

#reinitialize(path_obj = @path_obj) ⇒ Object



122
123
124
125
126
127
128
129
130
131
# File 'lib/bootsnap/load_path_cache/cache.rb', line 122

def reinitialize(path_obj = @path_obj)
  @mutex.synchronize do
    @path_obj = path_obj
    ChangeObserver.register(@path_obj, self)
    @index = {}
    @dirs = {}
    @generated_at = now
    push_paths_locked(*@path_obj)
  end
end

#unshift_paths(sender, *paths) ⇒ Object



110
111
112
113
114
# File 'lib/bootsnap/load_path_cache/cache.rb', line 110

def unshift_paths(sender, *paths)
  return unless sender == @path_obj

  @mutex.synchronize { unshift_paths_locked(*paths) }
end