Ruby 3.2.0 RC 1 リリース

Ruby 3.2.0 Release Candidate 1 が公開されました。Ruby 3.2では多くの機能を追加しています。

WASIベースのWebAssemblyサポート

WASIベースのWebAssemblyへのコンパイルがサポートされました。これにより、ブラウザやサーバーレスエッジ環境、その他のWebAssembly/WASI環境でCRubyのバイナリが利用できるようになります。現在この移植版はThread API以外のbasic testとbootstrap testをパスしています。

Background

もともとWebAssembly (Wasm)が導入されたのは、プログラムをブラウザの上で安全かつ高速に実行するためでした。しかし、様々な環境で安全かつ効率的にプログラムを実行するという目的は、Webだけでなく一般的なアプリケーションで長らく求められていたものです

WASI (The WebAssembly System Interface)はそのようなユースケースのために設計されました。そのようなアプリケーションはOSと通信する必要がありますが、WebAssembly自体はシステムインターフェイスを持たないVMの上で実行されます。WASIはこのシステムインターフェイスを規格化します。

RubyのWebAssembly/WASIサポートは、このようなプロジェクトを活用することを狙っています。これにより、Ruby開発者がそのような有望なプラットフォームで動くアプリケーションを書けるようになります。

ユースケース

このサポートは、開発者がCRubyをWebAssembly環境で利用することを促進します。たとえば、TryRuby playgroundのCRubyサポートです。CRubyをウェブブラウザの上で試すことができるようになりました。

技術的な話

現時点のWASIとWebAssemblyには、Fiberや例外やGCを実装するための機能に一部足りないものがあります。CRubyではこのギャップを埋めるために、ユーザランドで実行を制御できるAsyncifyというバイナリ変換技術を使っています。

さらに、WASIの上にVFSを実装しました。これにより、Rubyアプリを単一の.wasmファイルに容易にパッケージ化できます。Rubyアプリの配布が少しかんたんになります。

参考文献

ReDoSに対するRegexpの改善

正規表現マッチングは予想外に時間がかかることがあることが知られています。もし信頼できない入力に対して非効率な可能性のある正規表現をマッチしていると、Denial of Service攻撃を効率的にできてしまう可能性があります(正規表現DoS、ReDoSなどと言われます)。

Ruby 3.2では、ReDoSを大幅に軽減する2つの改善を導入しました。

Regexpのマッチングアルゴリズムの改善

Ruby 3.2から、Regexpのマッチングアルゴリズム自体がメモ化の最適化によって大幅に改善されました。

# 次のマッチングはRuby 3.1では10秒かかりますが、Ruby 3.2では0.003秒で終わります

/^a*b?a*$/ =~ "a" * 50000 + "x"

このアルゴリズムの改善で、ほとんどの(我々の実験では90%程度の)正規表現が線形時間でマッチ判定できるようになります。

(プレビューリリースのユーザへ:この最適化は、マッチングのたびに入力長に比例したメモリを消費することがあります。このメモリ確保は通常遅延され、また、通常の正規表現であれば入力長のたかだか10倍程度のメモリを消費するだけなので、実用上の問題は発生しないと考えています。もし実アプリの正規表現マッチングでメモリ不足に陥った場合は報告してください)

提案チケットは https://bugs.ruby-lang.org/issues/19104 です。

Regexpのタイムアウトの導入

上記の最適化は、ある種の正規表現には適用できません。たとえば、後方参照や先読み・後読みのような発展的機能や、非常に大きい固定回数繰り返しを使っている場合には適用されません。この場合の対策として、正規表現マッチングのタイムアウト機能が導入されました。

Regexp.timeout = 1.0

/^a*b?a*()\1$/ =~ "a" * 50000 + "x"
#=> 1秒後にRegexp::TimeoutError

なお、Regexp.timeoutはグローバルな設定です。もし一部の特別な正規表現にだけ異なるタイムアウトを設定したい場合は、Regexp.newtimeoutキーワードを指定してください。

Regexp.timeout = 1.0

# This regexp has no timeout
long_time_re = Regexp.new('^a*b?a*()\1$', timeout: Float::INFINITY)

long_time_re =~ "a" * 50000 + "x" # never interrupted

提案チケットは https://bugs.ruby-lang.org/issues/17837 です。

その他の主要な新機能

SyntaxSuggest

  • syntax_suggest の機能が Ruby に統合されました。syntax_suggest は、Ruby のコードの実行時に以下の例のようにエラーが起きた場所を容易に発見する機能を提供します。

    Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?
    
      1  class Dog
    > 2    defbark
    > 4    end
      5  end
    

    [Feature #18159]

ErrorHighlight

  • TypeErrorとArgumentErrorの引数を下線表示するようになりました
test.rb:2:in `+': nil can't be coerced into Integer (TypeError)

sum = ary[0] + ary[1]
               ^^^^^^

言語機能

  • Anonymous rest and keyword rest arguments can now be passed as arguments, instead of just used in method parameters. [Feature #18351]

      def foo(*)
        bar(*)
      end
      def baz(**)
        quux(**)
      end
    
  • A proc that accepts a single positional argument and keywords will no longer autosplat. [Bug #18633]

    proc{|a, **k| a}.call([1, 2])
    # Ruby 3.1 and before
    # => 1
    # Ruby 3.2 and after
    # => [1, 2]
    
  • Constant assignment evaluation order for constants set on explicit objects has been made consistent with single attribute assignment evaluation order. With this code:

      foo::BAR = baz
    

    foo is now called before baz. Similarly, for multiple assignments to constants, left-to-right evaluation order is used. With this code:

        foo1::BAR1, foo2::BAR2 = baz1, baz2
    

    The following evaluation order is now used:

    1. foo1
    2. foo2
    3. baz1
    4. baz2

    [Bug #15928]

  • Find patternが実験的機能ではなくなりました。 [Feature #18585]

  • Methods taking a rest parameter (like *args) and wishing to delegate keyword arguments through foo(*args) must now be marked with ruby2_keywords (if not already the case). In other words, all methods wishing to delegate keyword arguments through *args must now be marked with ruby2_keywords, with no exception. This will make it easier to transition to other ways of delegation once a library can require Ruby 3+. Previously, the ruby2_keywords flag was kept if the receiving method took *args, but this was a bug and an inconsistency. A good technique to find the potentially-missing ruby2_keywords is to run the test suite, for where it fails find the last method which must receive keyword arguments, use puts nil, caller, nil there, and check each method/block on the call chain which must delegate keywords is correctly marked as ruby2_keywords. [Bug #18625] [Bug #16466]

      def target(**kw)
      end
    
      # Accidentally worked without ruby2_keywords in Ruby 2.7-3.1, ruby2_keywords
      # needed in 3.2+. Just like (*args, **kwargs) or (...) would be needed on
      # both #foo and #bar when migrating away from ruby2_keywords.
      ruby2_keywords def bar(*args)
        target(*args)
      end
    
      ruby2_keywords def foo(*args)
        bar(*args)
      end
    
      foo(k: 1)
    

パフォーマンスの改善

YJIT

  • YJIT now supports both x86-64 and arm64/aarch64 CPUs on Linux, MacOS, BSD and other UNIX platforms.
    • This release brings support for Mac M1/M2, AWS Graviton and Raspberry Pi 4 ARM64 processors.
  • Building YJIT requires Rust 1.58.0+. [Feature #18481]
    • In order to ensure that CRuby is built with YJIT, please install rustc >= 1.58.0 and run ./configure with --enable-yjit.
    • Please reach out to the YJIT team should you run into any issues.
  • Physical memory for JIT code is lazily allocated. Unlike Ruby 3.1, the RSS of a Ruby process is minimized because virtual memory pages allocated by --yjit-exec-mem-size will not be mapped to physical memory pages until actually utilized by JIT code.
  • Introduce Code GC that frees all code pages when the memory consumption by JIT code reaches --yjit-exec-mem-size.
    • RubyVM::YJIT.runtime_stats returns Code GC metrics in addition to existing inline_code_size and outlined_code_size keys: code_gc_count, live_page_count, freed_page_count, and freed_code_size.
  • Most of the statistics produced by RubyVM::YJIT.runtime_stats are now available in release builds.
    • Simply run ruby with --yjit-stats to compute stats (incurs some run-time overhead).
  • YJIT is now optimized to take advantage of object shapes. [[Feature #18776]]
  • Take advantage of finer-grained constant invalidation to invalidate less code when defining new constants. [[Feature #18589]]

PubGrub

  • Bundler 2.4 は利用する依存解決ライブラリを Molinillo から PubGrub に変更しました。

    • PubGrub は次世代の Dart 言語のパッケージマネージャである pub で使われている次世代の依存解決アルゴリズムです。
    • この変更により、bundler を実行後に異なるライブラリの依存解決結果となる可能性があります。もし、不具合や気になる点を見つけた方は RubyGems/Bundler issues までご報告ください。
  • RubyGems は Ruby 3.2 では引き続き Molinillo ライブラリを利用しています。今後、RubyGems も PubGrub 変更し、Bundler と同じライブラリを使う予定です。

その他の注目すべき 3.1 からの変更点

  • Hash
    • Hash#shift now always returns nil if the hash is empty, instead of returning the default value or calling the default proc. [Bug #16908]
  • MatchData
  • Module
  • Proc
  • Refinement
  • RubyVM::AbstractSyntaxTree
    • Add error_tolerant option for parse, parse_file and of. [[Feature #19013]]
  • Set
    • Set is now available as a builtin class without the need for require "set". [Feature #16989] It is currently autoloaded via the Set constant or a call to Enumerable#to_set.
  • String
    • String#byteindex and String#byterindex have been added. [Feature #13110]
    • Update Unicode to Version 15.0.0 and Emoji Version 15.0. [[Feature #18639]] (also applies to Regexp)
    • String#bytesplice has been added. [Feature #18598]
  • Struct
    • A Struct class can also be initialized with keyword arguments without keyword_init: true on Struct.new [Feature #16806]

互換性に関する変更

Removed constants

The following deprecated constants are removed.

Removed methods

The following deprecated methods are removed.

標準添付ライブラリの互換性に関する変更

3rd パーティライブラリのソースコード同梱廃止

  • libyamllibffi のような 3rd パーティのライブラリのソースコードの同梱を廃止しました

    • Psych に同梱していた libyaml のソースコードは削除されました。ユーザーは自身で Ubuntu や Debian プラットフォームなら libyaml-dev パッケージをインストールする必要があります。このパッケージ名称はプラットフォームごとに異なります。

    • Fiddle に同梱していた libffi のソースコードも削除されました

  • Psych と fiddle には特定バージョンの libyamllibffi のソースコードを静的リンクするための機能が追加されました。libyaml-0.2.5 をリンクしてビルドする場合は以下のように実行します。

      $ ./configure --with-libyaml-source-dir=/path/to/libyaml-0.2.5
    

    同様に、libffi-3.4.4 を fiddle にリンクする場合は以下のように実行します。

      $ ./configure --with-libffi-source-dir=/path/to/libffi-3.4.4
    

C API updates

Updated C APIs

The following APIs are updated.

  • PRNG update rb_random_interface_t updated and versioned. Extension libraries which use this interface and built for older versions. Also init_int32 function needs to be defined.

Removed C APIs

The following deprecated APIs are removed.

  • rb_cData variable.
  • “taintedness” and “trustedness” functions. [Feature #16131]

標準添付ライブラリのアップデート

  • 以下の default gems のバージョンがアップデートされました。
    • RubyGems 3.4.0.dev
    • benchmark 0.2.1
    • bigdecimal 3.1.3
    • bundler 2.4.0.dev
    • cgi 0.3.6
    • date 3.3.0
    • delegate 0.3.0
    • did_you_mean 1.6.2
    • digest 3.1.1
    • drb 2.1.1
    • erb 4.0.2
    • error_highlight 0.5.1
    • etc 1.4.1
    • fcntl 1.0.2
    • fiddle 1.1.1
    • fileutils 1.7.0
    • forwardable 1.3.3
    • getoptlong 0.2.0
    • io-console 0.5.11
    • io-nonblock 0.2.0
    • io-wait 0.3.0.pre
    • ipaddr 1.2.5
    • irb 1.5.1
    • json 2.6.2
    • logger 1.5.2
    • mutex_m 0.1.2
    • net-http 0.3.1
    • net-protocol 0.2.0
    • nkf 0.1.2
    • open-uri 0.3.0
    • openssl 3.1.0.pre
    • optparse 0.3.0
    • ostruct 0.5.5
    • pathname 0.2.1
    • pp 0.4.0
    • pstore 0.1.2
    • psych 5.0.0
    • racc 1.6.1
    • rdoc 6.5.0
    • reline 0.3.1
    • resolv 0.2.2
    • securerandom 0.2.1
    • set 1.0.3
    • stringio 3.0.3
    • syntax_suggest 1.0.1
    • timeout 0.3.1
    • tmpdir 0.1.3
    • tsort 0.1.1
    • un 0.2.1
    • uri 0.12.0
    • win32ole 1.8.9
    • zlib 3.0.0
  • 以下の bundled gems のバージョンがアップデートされました。
    • minitest 5.16.3
    • power_assert 2.0.2
    • test-unit 3.5.5
    • net-ftp 0.2.0
    • net-imap 0.3.1
    • net-pop 0.1.2
    • net-smtp 0.3.3
    • rbs 2.8.1
    • typeprof 0.21.3
    • debug 1.7.0

その他詳細については、NEWS ファイルまたはコミットログを参照してください。

なお、こうした変更により、Ruby 3.1.0 以降では 2846 個のファイルに変更が加えられ、203950 行の追加と 127153 行の削除が行われました !

ダウンロード

  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0-rc1.tar.gz

    SIZE: 20253652
    SHA1: 9b45af61ef1ae3c21ab88d7c9e30b80060116ac3
    SHA256: 3bb9760c1ac1b66416aaa4899809f6ccd010e57038eaaeca19a383fd56275dac
    SHA512: 798157d785ebae94cb128d3c134fa35e0e90c654972e531cb6562823042f3fb68a270226f7b1cf0c42572ef2b1488a1a3e44f88389ad2a6f9ca4b280a2a8e759
    
  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0-rc1.tar.xz

    SIZE: 14934012
    SHA1: 5576e304786d466410f27a345dc1cb66f2c773f6
    SHA256: 0d45b3af14e84337882a2021235a091ae5dcfc0baaf31dccc479b71d96dd07bc
    SHA512: d38fcb1e09eb9984f3b2347e65ae7406129c2578d068a25d33b5b4f021ec3b567a9abe56c2acbec6d07a3c2b4bc7b485dbd330cbfbb3a96350f60a2bb94d016e
    
  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0-rc1.zip

    SIZE: 24473024
    SHA1: 8fdc85363ce61e0b8f04da36e709d49028d04a75
    SHA256: 7ff32473be108534548e401aaa9092c37a27f73323ea4091c33901c714c87ee5
    SHA512: 07adf6a9c89fdcf420e7b131f40f2b1f4aca036aa6f28539ade26ca552f84a75e0698f77a8b774d2ea52b8c756c4982ef319bda5afa786c081a31dd9873c5ef7
    

Ruby とは

Rubyはまつもとゆきひろ (Matz) によって1993年に開発が始められ、今もオープンソースソフトウェアとして開発が続けられています。Rubyは様々なプラットフォームで動き、世界中で、特にWebアプリケーション開発のために使われています。