Module: Ryac::Packer
- Defined in:
- lib/ryac/packer.rb,
sig/ryac/packer.rbs
Overview
Self-extracting output encoding, applied after minification. The emitted
file is a short plain-Ruby stub followed by the real program compressed
after END; running it inflates the bytes and evals them with $0 as
the file name, so a $PROGRAM_NAME == __FILE__ launcher still fires and
relative requires resolve as they would from the plain artifact.
This is an output format, not a stage: stages rewrite Ruby into equivalent Ruby and re-parse to a fixed point, while a packed file is opaque bytes. Two formats:
:self — an LZSS decoder (4KB window, 3..18-byte matches) inlined in
the stub; no require at all, runs on any Ruby.
:zlib — deflate via the zlib default gem; smaller, but dead on a Ruby
built without it.
Constant Summary collapse
- FORMATS =
%i[self zlib].freeze
- SELF_STUB =
's=DATA.binmode.read;o="".b;i=0;while i<s.size;f=s.getbyte i;i+=1;' \ '8.times{break if i>=s.size;if f&1>0;o<<s.getbyte(i);i+=1;else;' \ 'a=s.getbyte i;b=s.getbyte i+1;i+=2;d=(a<<4|b>>4)+1;l=(b&15)+3;' \ 'l.times{o<<o.getbyte(o.size-d)};end;f>>=1};end;' \ 'eval o.force_encoding("UTF-8"),nil,$0' \ "\n__END__\n"
- ZLIB_STUB =
'require"zlib";eval Zlib.inflate(DATA.binmode.read).force_encoding("UTF-8"),nil,$0' \ "\n__END__\n"
Class Method Summary collapse
- .pack(source, format) ⇒ String
-
.reject_data_readers(source) ⇒ void
The stub consumes the packed file's one END/DATA stream, so a program carrying its own data section or reading DATA would read compressed garbage instead.
- .resolve_format(value) ⇒ Symbol
Class Method Details
.pack(source, format) ⇒ String
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/ryac/packer.rb', line 38 def self.pack(source, format) reject_data_readers(source) case resolve_format(format) when :zlib require 'zlib' ZLIB_STUB.b + Zlib::Deflate.deflate(source, Zlib::BEST_COMPRESSION) else SELF_STUB.b + lzss_compress(source) end end |
.reject_data_readers(source) ⇒ void
This method returns an undefined value.
The stub consumes the packed file's one END/DATA stream, so a program carrying its own data section or reading DATA would read compressed garbage instead.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/ryac/packer.rb', line 52 def self.reject_data_readers(source) result = Prism.parse(source) offending = result.data_loc ? '__END__' : nil unless offending AstUtils.each_node(result.value) do |node| case node when Prism::ConstantReadNode offending = 'DATA' if node.name == :DATA when Prism::ConstantPathNode offending = 'DATA' if node.parent.nil? && node.name == :DATA end break if offending end end return unless offending raise MinifyError, "cannot pack: the program uses #{offending}, and the self-extracting stub owns the packed file's data section" end |