module Bootsnap::CompileCache::YAML
def cache_dir=(cache_dir)
def cache_dir=(cache_dir) @cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}yaml" : "#{cache_dir}-yaml" end
def init!
def init! require "yaml" require "msgpack" require "date" @implementation = ::YAML::VERSION >= "4" ? Psych4 : Psych3 if @implementation::Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file) @implementation::Patch.send(:remove_method, :unsafe_load_file) end unless const_defined?(:NoTagsVisitor) visitor = Class.new(Psych::Visitors::NoAliasRuby) do def visit(target) if target.tag raise UnsupportedTags, "YAML tags are not supported: #{target.tag}" end super end end const_set(:NoTagsVisitor, visitor) end # MessagePack serializes symbols as strings by default. # We want them to roundtrip cleanly, so we use a custom factory. # see: https://github.com/msgpack/msgpack-ruby/pull/122 factory = MessagePack::Factory.new factory.register_type( 0x00, Symbol, packer: :to_msgpack_ext, unpacker: EncodingAwareSymbols.method(:unpack).to_proc, ) if defined? MessagePack::Timestamp factory.register_type( MessagePack::Timestamp::TYPE, # or just -1 Time, packer: MessagePack::Time::Packer, unpacker: MessagePack::Time::Unpacker, ) marshal_fallback = { packer: ->(value) { Marshal.dump(value) }, unpacker: ->(payload) { Marshal.load(payload) }, } { Date => 0x01, Regexp => 0x02, }.each do |type, code| factory.register_type(code, type, marshal_fallback) end end self.msgpack_factory = factory self.supported_options = [] params = ::YAML.method(:load).parameters if params.include?([:key, :symbolize_names]) supported_options << :symbolize_names end if params.include?([:key, :freeze]) && factory.load(factory.dump("yaml"), freeze: true).frozen? supported_options << :freeze end supported_options.freeze end
def install!(cache_dir)
def install!(cache_dir) self.cache_dir = cache_dir init! ::YAML.singleton_class.prepend(@implementation::Patch) end
def patch
def patch @implementation::Patch end
def precompile(path)
def precompile(path) return false unless CompileCache::YAML.supported_internal_encoding? CompileCache::Native.precompile( cache_dir, path.to_s, @implementation, ) end
def strict_load(payload)
def strict_load(payload) ast = ::YAML.parse(payload) return ast unless ast loader = ::Psych::ClassLoader::Restricted.new(["Symbol"], []) scanner = ::Psych::ScalarScanner.new(loader) NoTagsVisitor.new(scanner, loader).visit(ast) end
def supported_internal_encoding?
UTF-8, US-ASCII and BINARY. So if Encoding.default_internal is set to anything else
Psych coerce strings to `Encoding.default_internal` but Message Pack only support
def supported_internal_encoding? SUPPORTED_INTERNAL_ENCODINGS.include?(Encoding.default_internal) end
def visit(target)
def visit(target) if target.tag raise UnsupportedTags, "YAML tags are not supported: #{target.tag}" end super end