Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CurrentVersion.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<MajorVersion>4</MajorVersion>
<MinorVersion>0</MinorVersion>
<MicroVersion>1</MicroVersion>
<MicroVersion>2</MicroVersion>
<ReleaseLevel>final</ReleaseLevel>
<ReleaseSerial>0</ReleaseSerial>

Expand Down
88 changes: 88 additions & 0 deletions Docs/releases/v4.0.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
**IronRuby 4.0.2** runs a Rails 8.1 app with SQLite on Windows, is about 1.2 times as fast
across IronRuby's micro benchmarks and starts in 1.25 s instead of 1.86 s, serves TLS through
net/http and Puma, and calls .NET the way Ruby 4 code is written. The legacy parser and the
Ruby 1.9 library are gone. The interpreter is now called `ironruby`, as JRuby's is `jruby`,
and every download comes with a signed build provenance attestation.

### Download

Self-contained: unpack one and run `./ironruby`, `./irb.sh` and `./igem.sh` - on Windows
`ironruby.cmd`, `irb.cmd` and `igem.cmd`. No .NET to install. `ir.sh` and `ir.cmd` still
work.

- **Windows x64**: `ironruby-4.0.2-win-x64.zip`, with Windows Forms, WPF, `ironrubyw.exe`
and `Samples\Notepad\notepad.cmd`
- **Linux x64**: `ironruby-4.0.2-linux-x64.tar.gz`
- **macOS arm64**: `ironruby-4.0.2-osx-arm64.tar.gz`, built in CI, not tested there

`SHA256SUMS` lists the checksums. Each archive and `SHA256SUMS` has a signed build
provenance attestation, which says which workflow built it, from which commit, in which run:

```
gh attestation verify ironruby-4.0.2-linux-x64.tar.gz --repo Largo/ironruby
```

### New

- **Rails 8.1 with SQLite on Windows** (#34). `gem install rails`, `rails new`,
`bundle install`, a scaffold, migrations, importmap with Turbo and Stimulus, and
`bin/rails server` on Puma and sqlite3, as a Windows user would run them. It took
`File#flock` on Windows, IO readiness without libc, Bundler's `platforms:` for IronRuby,
binstubs that start IronRuby, and a dozen more fixes, listed in
[Docs/rails-on-windows.md](https://github.com/Largo/ironruby/blob/v4.0.2/Docs/rails-on-windows.md).
In a sweep of popular gems on Windows, 81 give the same output as CRuby byte for byte,
among them rubocop, standard, rspec, pry, prawn, sprockets and listen.
- **Faster** (#22). IronRuby's 50 micro benchmarks run in 11.2 s instead of 26.1 s (geometric
mean 1.21x): switching fibers is 7.1x as fast, `raise`/`rescue` 5.5x, keyword arguments
about 9x, near CRuby's time. `ironruby -e 1` starts in about 1.25 s instead of 1.86 s.
- **TLS** (#28, #32, #33). `OpenSSL::SSL::SSLSocket` is non-blocking, so net/http's open,
read and write timeouts work unmodified, for RubyGems too; certificates are verified
against the context's CAs, with `verify_callback`. Puma serves `ssl://`.
- **Calling .NET** (#26, #31, #25). Keyword arguments bind to parameters by name, a block
goes to the trailing delegate parameter (`list.Sort { |a, b| b <=> a }`, `Task.Run { }`),
and the split .NET libraries load on demand, so `System::Console` just works. `super` in
`initialize` runs the .NET base constructor. Stores into a boxed struct's field stick,
zlib works on Windows, and so do `Process.kill` and `File::NULL`.
- **Ruby 4.0** (#23, #30). Many core classes now behave as CRuby 4.0's, found by running
IronRuby and CRuby side by side and by ruby/spec. `Rational` and `Complex` are ports of
CRuby 4.0's, and `Pathname` is CRuby 4.0's own.
- **prism 2.0** (#27). IronRuby follows ruby/prism's new serialization, and deeply nested
code runs instead of overflowing the stack.

### Changed

- **The interpreter is `ironruby`** (#35), and so is RbConfig's `ruby_install_name`: the
binstubs Bundler and Rails write start `#!/usr/bin/env ironruby`, a line Ruby runs as
Ruby. `ir`, `ir.sh` and `ir.cmd` stay as its short name. On Windows, `ironrubyw.exe` is
the same interpreter without a console window, as `rubyw.exe` is CRuby's; the Notepad
sample starts with it.
- **`ironruby` with no script reads the program from stdin** (#35), as `ruby` does, at a terminal
too, and `$0` is `"-"`. It used to start the DLR's interactive console; irb is `irb` or
`iirb`.
- **Prism is the only parser** (#34). `-X:UsePrism` is still accepted and does nothing.
- **Gems written for prism 1.x or bigdecimal 2.x and 3.x activate** with the built-in
prism 2.0 and bigdecimal 4.0 (#34): rubocop, standard, minitest 6 and prawn among them.
- **Windows**: file names, `ENV`, `ARGV` and the default external encoding are UTF-8, as in
CRuby; `Dir.pwd` keeps a short 8.3 path as it was given; `Dir.home` uses `/`; local time
follows daylight saving time (#19).
- **The Ruby 1.9 library is removed** (#29, #30), including its `cgi` code with known
vulnerabilities and a `win32/sspi` that wrote past its buffers on 64-bit Windows.

### Security and auditing

- Signed build provenance for every published file, made by GitHub's `actions/attest` in
the job that built it (#36).
- The repository holds no programs any more: 18 test fixtures and sample DLLs from 2016,
which nothing built or shipped, are gone, and CI fails if one comes back (#36). The
upstream checkouts the build uses are pinned (#29).

### Thanks

[@khasinski](https://github.com/khasinski) for `File#flock` on macOS (#17) and for
`ir -S <gem executable>` finding RubyGems' binstubs (#18).

### Known limits

- The release targets .NET 8, whose support ends on 10 November 2026.
- macOS is built, not tested. The full status is in the
[README](https://github.com/Largo/ironruby#status).
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ brought back to life: it **builds and runs on .NET 8 and 10**, and it parses Rub
**[prism](https://github.com/ruby/prism), CRuby's own parser**, instead of the hand-ported
Ruby 1.9 grammar it shipped with in 2011.

**IronRuby 4.0 is the first stable release since 1.1.3 in 2011**, and 4.0.1 is its latest.
**IronRuby 4.0 is the first stable release since 1.1.3 in 2011**, and 4.0.2 is its latest.
The downloads are self-contained: unpack one and run it, there is no .NET to install.

**The compatibility target is Ruby 4.0.** IronRuby aims to run what CRuby 4.0 runs, with
Expand All @@ -22,12 +22,12 @@ not kept.

| | Download | |
|---|---|---|
| **Windows** x64 | [ironruby-4.0.1-win-x64.zip](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-win-x64.zip) | with Windows Forms, WPF and the Notepad sample |
| **Linux** x64 | [ironruby-4.0.1-linux-x64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-linux-x64.tar.gz) | |
| **macOS** arm64 | [ironruby-4.0.1-osx-arm64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-osx-arm64.tar.gz) | built in CI, not tested there |
| **Windows** x64 | [ironruby-4.0.2-win-x64.zip](https://github.com/Largo/ironruby/releases/download/v4.0.2/ironruby-4.0.2-win-x64.zip) | with Windows Forms, WPF and the Notepad sample |
| **Linux** x64 | [ironruby-4.0.2-linux-x64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.2/ironruby-4.0.2-linux-x64.tar.gz) | |
| **macOS** arm64 | [ironruby-4.0.2-osx-arm64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.2/ironruby-4.0.2-osx-arm64.tar.gz) | built in CI, not tested there |

[SHA256SUMS](https://github.com/Largo/ironruby/releases/download/v4.0.1/SHA256SUMS) ·
[release notes](https://github.com/Largo/ironruby/releases/tag/v4.0.1) ·
[SHA256SUMS](https://github.com/Largo/ironruby/releases/download/v4.0.2/SHA256SUMS) ·
[release notes](https://github.com/Largo/ironruby/releases/tag/v4.0.2) ·
[all releases](https://github.com/Largo/ironruby/releases) · [getting started](#binary-releases)

[![IronRuby 4.0 launch video: the title, an irb session, Windows Forms and the Notepad sample](media/launch/teaser.webp)](https://github.com/Largo/ironruby/releases/download/v4.0.0/ironruby-4-launch.mp4)
Expand Down Expand Up @@ -64,7 +64,7 @@ drives it in CI with nobody at the keyboard.*
```console
$ ./irb.sh
irb(main):001> RUBY_DESCRIPTION
=> "IronRuby 4.0.1 (4.0.0) on .NET 8.0.31 [x86_64-linux]"
=> "IronRuby 4.0.2 (4.0.0) on .NET 8.0.31 [x86_64-linux]"
irb(main):002> def fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2)
=> :fib
irb(main):003> (1..10).map { fib(_1) }
Expand Down Expand Up @@ -177,7 +177,7 @@ releases for that Ruby version (4.0.0, 4.0.1, ...) and is not CRuby's patch leve
release notes say which CRuby release the specs were checked against. The last release of
the original project was 1.1.3, for Ruby 1.9.2, in 2011.

`IRONRUBY_VERSION` is the full version (`4.0.1`, or `4.0.0-preview1` for a pre-release),
`IRONRUBY_VERSION` is the full version (`4.0.2`, or `4.0.0-preview1` for a pre-release),
`RUBY_ENGINE_VERSION` the same without the pre-release suffix, and `RUBY_VERSION` the Ruby
version (`4.0.0`).

Expand Down Expand Up @@ -500,19 +500,19 @@ processes, socket addresses and Puma were fixed there in 4.0.1 (#12, #13).

[Releases](https://github.com/Largo/ironruby/releases) carry a ready-to-run IronRuby for
Linux x64, Windows x64 and macOS arm64 (not tested in CI); the [table at the top](#ironruby-modernized)
links the ones for 4.0.1. They are self-contained, so no .NET has to be installed. Unpack
links the ones for 4.0.2. They are self-contained, so no .NET has to be installed. Unpack
one and run the launchers in it:

```console
$ tar xzf ironruby-4.0.1-linux-x64.tar.gz && cd ironruby-4.0.1-linux-x64
$ ./ir.sh -e 'puts RUBY_DESCRIPTION'
$ tar xzf ironruby-4.0.2-linux-x64.tar.gz && cd ironruby-4.0.2-linux-x64
$ ./ironruby -e 'puts RUBY_DESCRIPTION'
$ ./irb.sh
$ ./igem.sh install rack
```

4.0.1 has `ir.sh` and `ir.cmd`; releases after it also have `ironruby` and `ironruby.cmd`
(see above), and `ir.sh` / `ir.cmd` keep working there.
On Windows, unzip it and use `ir.cmd`, `irb.cmd` and `igem.cmd`, or double-click
From 4.0.2 on, the archives have `ironruby` and `ironruby.cmd` (see above), and `ir.sh` /
`ir.cmd` keep working; 4.0.1 and earlier have only those.
On Windows, unzip it and use `ironruby.cmd`, `irb.cmd` and `igem.cmd`, or double-click
`Samples\Notepad\notepad.cmd`. The archive has the same layout as a checkout, with the
Release build in `Src/Console/bin/Release/net8.0`, so everything above applies to it
unchanged. `irubyc` works too, but it still needs the .NET SDK to compile the application.
Expand All @@ -524,7 +524,7 @@ bundles, Microsoft's license and third-party notices.
`.github/workflows/release.yml` builds all three and publishes them when a `v*` tag is
pushed; the [launch video](media/launch) is rendered and added by hand.

Releases after 4.0.1 carry a signed [build provenance attestation](https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)
Releases from 4.0.2 on carry a signed [build provenance attestation](https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)
for each archive and for `SHA256SUMS`, made by GitHub's `actions/attest` in the job that
built the file. It says
which workflow built the file, from which commit, in which run, and that the file is
Expand Down Expand Up @@ -604,7 +604,7 @@ Windows machine, keeping the same relative layout (`ir.cmd` finds the binaries u
```
C:\ir> set IR_CONFIG=Release
C:\ir> ir.cmd -e "puts RUBY_DESCRIPTION"
IronRuby 4.0.1 (4.0.0) on .NET 8.0.31 [x64-mswin64]
IronRuby 4.0.2 (4.0.0) on .NET 8.0.31 [x64-mswin64]
C:\ir> irb.cmd
C:\ir> set RUBY_EXE=C:\ir\ir.cmd
C:\ir> ir.cmd -Imspec/lib mspec/bin/mspec-run spec/language
Expand Down
3 changes: 2 additions & 1 deletion Src/Libraries/Builtins/FileOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ internal static int GetNativeFileDescriptor(RubyIO/*!*/ io) {
[RubyMethod("flock", BuildConfig = "FEATURE_FILESYSTEM")]
public static object FileLock(RubyFile/*!*/ self, [DefaultProtocol]int operation) {
self.RequireInitialized();
if (Path.DirectorySeparatorChar == '\\') {
if (OperatingSystem.IsWindows()) {
return WindowsFileLock(self, operation);
}
if (!Posix.IsFlockAvailable) {
Expand Down Expand Up @@ -1195,6 +1195,7 @@ private static extern bool UnlockFileEx(Microsoft.Win32.SafeHandles.SafeFileHand
/// the reason the Posix path above does. RubyGems takes one for every binstub it
/// writes, so without it `gem install` of any gem with an executable failed.
/// </summary>
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
private static object WindowsFileLock(RubyFile/*!*/ self, int operation) {
var fs = self.GetStream().BaseStream as FileStream;
if (fs == null) {
Expand Down
7 changes: 4 additions & 3 deletions Src/Libraries/Puma/MiniSsl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,12 @@ private static string FileText(RubyContext/*!*/ context, object path, string/*!*

// SChannel cannot serve with an ephemeral key: round-trip the certificate through PFX.
if (OperatingSystem.IsWindows()) {
using (leaf) {
// the ephemeral one is disposed once the PFX copy has replaced it
using (X509Certificate2 ephemeral = leaf) {
#if NET9_0_OR_GREATER
leaf = X509CertificateLoader.LoadPkcs12(leaf.Export(X509ContentType.Pfx), null);
leaf = X509CertificateLoader.LoadPkcs12(ephemeral.Export(X509ContentType.Pfx), null);
#else
leaf = new X509Certificate2(leaf.Export(X509ContentType.Pfx));
leaf = new X509Certificate2(ephemeral.Export(X509ContentType.Pfx));
#endif
}
}
Expand Down
5 changes: 3 additions & 2 deletions Src/Ruby/Builtins/Range.Subclass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ private Subclass(Range.Subclass/*!*/ range)
ImmediateClass = range.ImmediateClass.NominalClass;
}

// Marshal.load of a Range subclass: the class travels in the SerializationInfo
protected Subclass(System.Runtime.Serialization.SerializationInfo/*!*/ info, System.Runtime.Serialization.StreamingContext context)
// Marshal.load of a Range subclass: the class travels in the SerializationInfo.
// Private, as for any sealed ISerializable type: serialization finds it anyway.
private Subclass(System.Runtime.Serialization.SerializationInfo/*!*/ info, System.Runtime.Serialization.StreamingContext context)
: base(info, context) {
ImmediateClass = (RubyClass)info.GetValue(RubyUtils.SerializationInfoClassKey, typeof(RubyClass));
}
Expand Down
10 changes: 5 additions & 5 deletions Src/Ruby/CurrentVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ namespace IronRuby {
public static class CurrentVersion {
public const int Major = 4;
public const int Minor = 0;
public const int Micro = 1;
public const int Micro = 2;
public const string ReleaseLevel = "final";
public const int ReleaseSerial = 0;

public const string ShortReleaseLevel = "final";

public const string Series = "4.0";
public const string DisplayVersion = "4.0.1";
public const string DisplayName = "IronRuby 4.0.1";
public const string DisplayVersion = "4.0.2";
public const string DisplayName = "IronRuby 4.0.2";

// Stays 4.0.0.0 across 4.0.x, so that what was built against one 4.0 release binds
// to the next; the file version and the display version say which release it is.
public const string AssemblyVersion = "4.0.0.0";

public const string AssemblyFileVersion = "4.0.1.0";
public const string AssemblyInformationalVersion = "IronRuby 4.0.1";
public const string AssemblyFileVersion = "4.0.2.0";
public const string AssemblyInformationalVersion = "IronRuby 4.0.2";

public static readonly Version Version = new Version(Major, Minor, Micro);
}
Expand Down
Loading