lib/bootsnap/compile_cache/iseq.rb



require 'bootsnap/bootsnap'
require 'zlib'

module Bootsnap
  module CompileCache
    module ISeq
      class << self
        attr_accessor :cache_dir
      end

      def self.input_to_storage(_, path)
        RubyVM::InstructionSequence.compile_file(path).to_binary
      rescue SyntaxError
        raise Uncompilable, 'syntax error'
      end

      def self.storage_to_output(binary)
        RubyVM::InstructionSequence.load_from_binary(binary)
      rescue RuntimeError => e
        if e.message == 'broken binary format'
          STDERR.puts "[Bootsnap::CompileCache] warning: rejecting broken binary"
          return nil
        else
          raise
        end
      end

      def self.input_to_output(_)
        nil # ruby handles this
      end

      module InstructionSequenceMixin
        def load_iseq(path)
          Bootsnap::CompileCache::Native.fetch(
            Bootsnap::CompileCache::ISeq.cache_dir,
            path.to_s,
            Bootsnap::CompileCache::ISeq
          )
        rescue RuntimeError => e
          if e.message =~ /unmatched platform/
            puts "unmatched platform for file #{path}"
          end
          raise
        rescue Errno::ERANGE
          STDERR.puts <<~EOF
            \x1b[31mError loading ISeq from cache for \x1b[1;34m#{path}\x1b[0;31m!
            You can likely fix this by running:
              \x1b[1;32mxattr -c #{path}
            \x1b[0;31m...but, first, please make sure \x1b[1;34m@burke\x1b[0;31m knows you ran into this bug!
            He will want to see the results of:
              \x1b[1;32m/bin/ls -l@ #{path}
            \x1b[0;31mand:
              \x1b[1;32mxattr -p user.aotcc.key #{path}\x1b[0m
          EOF
          raise
        end

        def compile_option=(hash)
          super(hash)
          Bootsnap::CompileCache::ISeq.compile_option_updated
        end
      end

      def self.compile_option_updated
        option = RubyVM::InstructionSequence.compile_option
        crc = Zlib.crc32(option.inspect)
        Bootsnap::CompileCache::Native.compile_option_crc32 = crc
      end

      def self.install!(cache_dir)
        Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
        Bootsnap::CompileCache::ISeq.compile_option_updated
        class << RubyVM::InstructionSequence
          prepend InstructionSequenceMixin
        end
      end
    end
  end
end