# frozen_string_literal: truerequire_relative('../explicit_require')moduleBootsnapmoduleLoadPathCacheclassCacheAGE_THRESHOLD=30# secondsdefinitialize(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=nilreinitializeend# 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".defload_dir(dir)reinitializeifstale?@mutex.synchronize{@dirs[dir]}end# { 'enumerator' => nil, 'enumerator.so' => nil, ... }BUILTIN_FEATURES=$LOADED_FEATURES.each_with_object({})do|feat,features|# Builtin features are of the form 'enumerator.so'.# All others include paths.nextunlessfeat.size<20&&!feat.include?('/')base=File.basename(feat,'.*')# enumerator.so -> enumeratorext=File.extname(feat)# .sofeatures[feat]=nil# enumerator.sofeatures[base]=nil# enumeratornextunless[DOT_SO,*DL_EXTENSIONS].include?(ext)DL_EXTENSIONS.eachdo|dl_ext|features["#{base}#{dl_ext}"]=nil# enumerator.bundleendend.freeze# Try to resolve this feature to an absolute path without traversing the# loadpath.deffind(feature,try_extensions: true)reinitializeif(@has_relative_paths&&dir_changed?)||stale?feature=feature.to_s.freezereturnfeatureifabsolute_path?(feature)iffeature.start_with?('./','../')returntry_extensions?expand_path(feature):File.expand_path(feature).freezeend@mutex.synchronizedox=search_index(feature,try_extensions: try_extensions)returnxifxreturnunlesstry_extensions# 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.raise(LoadPathCache::ReturnFalse,'',[])ifBUILTIN_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.caseFile.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_EXTENSIONSnil# Ruby allows specifying native extensions as '.so' even when DLEXT# is '.bundle'. This is where we handle that case.whenDOT_SOx=search_index(feature[0..-4]+DLEXT)returnxifxifDLEXT2x=search_index(feature[0..-4]+DLEXT2)returnxifxendelse# other, unknown extension. For example, `.rake`. Since we haven't# cached these, we legitimately need to run the load path search.raise(LoadPathCache::FallbackScan,'',[])endend# 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.raise(LoadPathCache::FallbackScan,'',[])if@development_modeendifRbConfig::CONFIG['host_os']=~/mswin|mingw|cygwin/defabsolute_path?(path)path[1]==':'endelsedefabsolute_path?(path)path.start_with?(SLASH)endenddefunshift_paths(sender,*paths)returnunlesssender==@path_obj@mutex.synchronize{unshift_paths_locked(*paths)}enddefpush_paths(sender,*paths)returnunlesssender==@path_obj@mutex.synchronize{push_paths_locked(*paths)}enddefreinitialize(path_obj=@path_obj)@mutex.synchronizedo@path_obj=path_objChangeObserver.register(self,@path_obj)@index={}@dirs={}@generated_at=nowpush_paths_locked(*@path_obj)endendprivatedefdir_changed?@prev_dir||=Dir.pwdif@prev_dir==Dir.pwdfalseelse@prev_dir=Dir.pwdtrueendenddefpush_paths_locked(*paths)@store.transactiondopaths.map(&:to_s).eachdo|path|p=Path.new(path)@has_relative_paths=trueifp.relative?nextifp.non_directory?expanded_path=p.expanded_pathentries,dirs=p.entries_and_dirs(@store)# push -> low precedence -> set only if unsetdirs.each{|dir|@dirs[dir]||=path}entries.each{|rel|@index[rel]||=expanded_path}endendenddefunshift_paths_locked(*paths)@store.transactiondopaths.map(&:to_s).reverse_eachdo|path|p=Path.new(path)nextifp.non_directory?expanded_path=p.expanded_pathentries,dirs=p.entries_and_dirs(@store)# unshift -> high precedence -> unconditional setdirs.each{|dir|@dirs[dir]=path}entries.each{|rel|@index[rel]=expanded_path}endendenddefexpand_path(feature)maybe_append_extension(File.expand_path(feature))enddefstale?@development_mode&&@generated_at+AGE_THRESHOLD<nowenddefnowProcess.clock_gettime(Process::CLOCK_MONOTONIC).to_iendifDLEXT2defsearch_index(f,try_extensions: true)iftry_extensionstry_index(f+DOT_RB)||try_index(f+DLEXT)||try_index(f+DLEXT2)||try_index(f)elsetry_index(f)endenddefmaybe_append_extension(f)try_ext(f+DOT_RB)||try_ext(f+DLEXT)||try_ext(f+DLEXT2)||fendelsedefsearch_index(f,try_extensions: true)iftry_extensionstry_index(f+DOT_RB)||try_index(f+DLEXT)||try_index(f)elsetry_index(f)endenddefmaybe_append_extension(f)try_ext(f+DOT_RB)||try_ext(f+DLEXT)||fendends=rand.to_s.force_encoding(Encoding::US_ASCII).freezeifs.respond_to?(:-@)if(-s).equal?(s)&&(-s.dup).equal?(s)||RUBY_VERSION>='2.7'deftry_index(f)if(p=@index[f])-(File.join(p,f).freeze)endendelsedeftry_index(f)if(p=@index[f])-File.join(p,f).untaintendendendelsedeftry_index(f)if(p=@index[f])File.join(p,f)endendenddeftry_ext(f)fifFile.exist?(f)endendendend