module Bootsnap::CompileCache::YAML

Constants

SUPPORTED_INTERNAL_ENCODINGS
Uncompilable
UnsupportedTags

Attributes

cache_dir[R]
implementation[R]
msgpack_factory[RW]
supported_options[RW]

Public Class Methods

cache_dir=(cache_dir) click to toggle source
# File lib/bootsnap/compile_cache/yaml.rb, line 22
def cache_dir=(cache_dir)
  @cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}yaml" : "#{cache_dir}-yaml"
end
init!() click to toggle source
# File lib/bootsnap/compile_cache/yaml.rb, line 57
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
install!(cache_dir) click to toggle source
# File lib/bootsnap/compile_cache/yaml.rb, line 36
def install!(cache_dir)
  self.cache_dir = cache_dir
  init!
  ::YAML.singleton_class.prepend(@implementation::Patch)
end
patch() click to toggle source
# File lib/bootsnap/compile_cache/yaml.rb, line 124
def patch
  @implementation::Patch
end
precompile(path) click to toggle source
# File lib/bootsnap/compile_cache/yaml.rb, line 26
def precompile(path)
  return false unless CompileCache::YAML.supported_internal_encoding?

  CompileCache::Native.precompile(
    cache_dir,
    path.to_s,
    @implementation,
  )
end
strict_load(payload) click to toggle source
# File lib/bootsnap/compile_cache/yaml.rb, line 128
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
supported_internal_encoding?() click to toggle source

Psych coerce strings to ‘Encoding.default_internal` but Message Pack only support UTF-8, US-ASCII and BINARY. So if Encoding.default_internal is set to anything else we can’t safely use the cache

# File lib/bootsnap/compile_cache/yaml.rb, line 45
def supported_internal_encoding?
  SUPPORTED_INTERNAL_ENCODINGS.include?(Encoding.default_internal)
end
visit(target) click to toggle source
Calls superclass method
# File lib/bootsnap/compile_cache/yaml.rb, line 69
def visit(target)
  if target.tag
    raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
  end

  super
end