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
15 changes: 14 additions & 1 deletion .github/actions/capiext/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ runs:
run: |
eval $(grep -e '^arch *=' -e '^ruby_version *=' -e '^DLEXT *=' Makefile |
sed 's/ *= */=/')
key=capiexts-${arch}-${ruby_version}
case "${ruby_version}" in
*+*) key=capiexts-${arch}-${ruby_version};;
*) key=;;
esac
echo version=$ruby_version >> $GITHUB_OUTPUT
echo key=$key >> $GITHUB_OUTPUT
echo DLEXT=$DLEXT >> $GITHUB_OUTPUT
working-directory: ${{ inputs.builddir }}
Expand All @@ -37,8 +41,10 @@ runs:
with:
path: ${{ inputs.builddir }}/spec/ruby/optional/capi/ext/
key: ${{ steps.config.outputs.key }}
if: ${{ steps.config.outputs.key }}

- name: Run test-spec with previous CAPI extension binaries
id: check
shell: bash
run: |
touch spec/ruby/optional/capi/ext/*.$DLEXT
Expand All @@ -48,3 +54,10 @@ runs:
DLEXT: ${{ steps.config.outputs.DLEXT }}
working-directory: ${{ inputs.builddir }}
if: ${{ steps.cache.outputs.cache-hit }}

- shell: bash
run: |
echo "::error::Change from ${prev} detected; bump up ABI version"
env:
prev: ${{ steps.config.outputs.version }}
if: ${{ always() && steps.check.outcome == 'failure' }}
3 changes: 2 additions & 1 deletion .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
os: macos-14
- test_task: check
os: macos-15
extra_checks: [capi]
capi_check: capi
- test_task: check
os: macos-13
Expand Down Expand Up @@ -166,7 +167,7 @@ jobs:
builddir: build
env:
RUBY_TESTOPTS: '-v --tty=no'
if: ${{ matrix.capi_check }}
if: ${{ contains(matrix.extra_checks, 'capi') }}

- uses: ./.github/actions/slack
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- test_task: test-bundled-gems
- test_task: check
os: ubuntu-24.04
capi_check: capi
extra_checks: [capi]
# ubuntu-24.04-arm jobs don't start on ruby/ruby as of 2025-09-04
#- test_task: check
# os: ubuntu-24.04-arm
Expand Down Expand Up @@ -160,7 +160,7 @@ jobs:
make: '$SETARCH make'
env:
RUBY_TESTOPTS: '-v --tty=no'
if: ${{ matrix.capi_check }}
if: ${{ contains(matrix.extra_checks, 'capi') }}

- uses: ./.github/actions/slack
with:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/yjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ jobs:
if: ${{ matrix.rust_version }}
run: rustup install ${{ matrix.rust_version }} --profile minimal

- name: Remove cargo
# Since this tests a `rustc` build for release, remove `cargo` to ensure
# that only `rustc` is used.
if: ${{ contains(matrix.configure, 'rustc') }}
run: sudo rm $(which -a cargo | uniq)

- name: Run configure
run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }}

Expand Down
16 changes: 11 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
# TODO(alan) notes about rust version requirements. Undecided yet.

[workspace]
members = ["zjit", "yjit"]
members = ["zjit", "yjit", "jit"]

[package]
name = "jit"
name = "ruby"
version = "0.0.0"
edition = "2024"
rust-version = "1.85.0"
Expand All @@ -18,7 +18,7 @@ zjit = { path = "zjit", optional = true }

[lib]
crate-type = ["staticlib"]
path = "jit.rs"
path = "ruby.rs"

[features]
disasm = ["yjit?/disasm", "zjit?/disasm"]
Expand Down
2 changes: 2 additions & 0 deletions common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ MAKE_LINK = $(MINIRUBY) -rfileutils -e "include FileUtils::Verbose" \
YJIT_RUSTC_ARGS = --crate-name=yjit \
--crate-type=staticlib \
--edition=2021 \
--cfg 'feature="stats_allocator"' \
-g \
-C lto=thin \
-C opt-level=3 \
Expand All @@ -276,6 +277,7 @@ YJIT_RUSTC_ARGS = --crate-name=yjit \
ZJIT_RUSTC_ARGS = --crate-name=zjit \
--crate-type=staticlib \
--edition=2024 \
--cfg 'feature="stats_allocator"' \
-g \
-C lto=thin \
-C opt-level=3 \
Expand Down
4 changes: 2 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -4019,9 +4019,9 @@ AS_IF([test x"$JIT_CARGO_SUPPORT" != "xno" -o \( x"$YJIT_SUPPORT" != "xno" -a x"
])
CARGO_BUILD_ARGS="--profile ${JIT_CARGO_SUPPORT} --features ${rb_cargo_features}"
AS_IF([test "${JIT_CARGO_SUPPORT}" = "dev"], [
RUST_LIB="target/debug/libjit.a"
RUST_LIB="target/debug/libruby.a"
], [
RUST_LIB="target/${JIT_CARGO_SUPPORT}/libjit.a"
RUST_LIB="target/${JIT_CARGO_SUPPORT}/libruby.a"
])
])

Expand Down
2 changes: 1 addition & 1 deletion defs/jit.mk
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ CARGO_VERBOSE = $(CARGO_VERBOSE_$(V))
# ld: warning: object file (target/debug/libjit.a(<libcapstone object>)) was built for
# newer macOS version (15.2) than being linked (15.0)
# This limits us to an older set of macOS API in the rust code, but we don't use any.
$(RUST_LIB): $(srcdir)/jit.rs
$(RUST_LIB): $(srcdir)/ruby.rs
$(Q)if [ '$(ZJIT_SUPPORT)' != no -a '$(YJIT_SUPPORT)' != no ]; then \
echo 'building YJIT and ZJIT ($(JIT_CARGO_SUPPORT:yes=release) mode)'; \
elif [ '$(ZJIT_SUPPORT)' != no ]; then \
Expand Down
6 changes: 6 additions & 0 deletions jit/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "jit"
version = "0.1.0"
edition = "2024"

[dependencies]
37 changes: 37 additions & 0 deletions jit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! Shared code between YJIT and ZJIT.

use std::sync::atomic::{AtomicUsize, Ordering};
use std::alloc::{GlobalAlloc, Layout, System};

#[global_allocator]
pub static GLOBAL_ALLOCATOR: StatsAlloc = StatsAlloc { alloc_size: AtomicUsize::new(0) };

pub struct StatsAlloc {
pub alloc_size: AtomicUsize,
}

unsafe impl GlobalAlloc for StatsAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.alloc_size.fetch_add(layout.size(), Ordering::SeqCst);
unsafe { System.alloc(layout) }
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.alloc_size.fetch_sub(layout.size(), Ordering::SeqCst);
unsafe { System.dealloc(ptr, layout) }
}

unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
self.alloc_size.fetch_add(layout.size(), Ordering::SeqCst);
unsafe { System.alloc_zeroed(layout) }
}

unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if new_size > layout.size() {
self.alloc_size.fetch_add(new_size - layout.size(), Ordering::SeqCst);
} else if new_size < layout.size() {
self.alloc_size.fetch_sub(layout.size() - new_size, Ordering::SeqCst);
}
unsafe { System.realloc(ptr, layout, new_size) }
}
}
7 changes: 5 additions & 2 deletions lib/bundler/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,8 @@ def converge_specs(specs)

specs.each do |s|
name = s.name
next if @gems_to_unlock.include?(name)

dep = @dependencies.find {|d| s.satisfies?(d) }
lockfile_source = s.source

Expand All @@ -1049,12 +1051,13 @@ def converge_specs(specs)

# Replace the locked dependency's source with the equivalent source from the Gemfile
s.source = replacement_source || default_source
next if s.source_changed?

source = s.source
next if @sources_to_unlock.include?(source.name)

# Path sources have special logic
if source.instance_of?(Source::Path) || source.instance_of?(Source::Gemspec) || (source.instance_of?(Source::Git) && !@gems_to_unlock.include?(name) && deps.include?(dep))
if source.is_a?(Source::Path)
new_spec = source.specs[s].first
if new_spec
s.runtime_dependencies.replace(new_spec.runtime_dependencies)
Expand Down Expand Up @@ -1136,7 +1139,7 @@ def lockfiles_equal?(current, proposed, preserve_unknown_sections)
def additional_base_requirements_to_prevent_downgrades(resolution_base)
return resolution_base unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
@originally_locked_specs.each do |locked_spec|
next if locked_spec.source.is_a?(Source::Path)
next if locked_spec.source.is_a?(Source::Path) || locked_spec.source_changed?

name = locked_spec.name
next if @changed_dependencies.include?(name)
Expand Down
2 changes: 2 additions & 0 deletions lib/bundler/ruby_dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def normalize_ruby_file(filename)
else
file_content.strip
end
rescue Errno::ENOENT
raise GemfileError, "Could not find version file #{filename}"
end
end
end
File renamed without changes.
10 changes: 10 additions & 0 deletions spec/bundler/bundler/ruby_dsl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,16 @@ class MockDSL
it_behaves_like "it stores the ruby version"
end
end

context "when the file does not exist" do
let(:ruby_version_file_path) { nil }
let(:ruby_version_arg) { nil }
let(:file) { "nonexistent.txt" }

it "raises an error" do
expect { subject }.to raise_error(Bundler::GemfileError, /Could not find version file nonexistent.txt/)
end
end
end
end
end
35 changes: 35 additions & 0 deletions spec/bundler/lock/git_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,39 @@

expect(lockfile).to include("securerandom (0.3.2)")
end

it "does not lock versions that don't exist in the repository when changing a GIT direct dep to a GEM direct dep" do
build_repo4 do
build_gem "ruby-lsp", "0.16.1"
end

path = lib_path("ruby-lsp")
revision = build_git("ruby-lsp", "0.16.2", path: path).ref_for("HEAD")

lockfile <<~L
GIT
remote: #{path}
revision: #{revision}
specs:
ruby-lsp (0.16.2)

PLATFORMS
#{lockfile_platforms}

DEPENDENCIES
ruby-lsp!

BUNDLED WITH
#{Bundler::VERSION}
L

gemfile <<~G
source "https://gem.repo4"
gem "ruby-lsp"
G

bundle "lock"

expect(lockfile).to include("ruby-lsp (0.16.1)")
end
end
3 changes: 3 additions & 0 deletions test/json/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
$LOAD_PATH.unshift(File.expand_path('../../../ext', __FILE__), File.expand_path('../../../lib', __FILE__))

require 'coverage'
Coverage.start

begin
require 'simplecov'
rescue LoadError
Expand Down
20 changes: 20 additions & 0 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,26 @@ def test(x)
}, insns: [:opt_case_dispatch]
end

def test_stack_overflow
assert_compiles 'nil', %q{
def recurse(n)
return if n == 0
recurse(n-1)
nil # no tail call
end

recurse(2)
recurse(2)
begin
recurse(20_000)
rescue SystemStackError
# Not asserting an exception is raised here since main
# thread stack size is environment-sensitive. Only
# that we don't crash or infinite loop.
end
}, call_threshold: 2
end

def test_invokeblock
assert_compiles '42', %q{
def test
Expand Down
2 changes: 2 additions & 0 deletions yjit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ publish = false # Don't publish to crates.io
# No required dependencies to simplify build process. TODO: Link to yet to be
# written rationale. Optional For development and testing purposes
capstone = { version = "0.13.0", optional = true }
jit = { version = "0.1.0", path = "../jit" }

# NOTE: Development builds select a set of these via configure.ac
# For debugging, `make V=1` shows exact cargo invocation.
Expand All @@ -24,3 +25,4 @@ disasm = ["capstone"]
# from cfg!(debug_assertions) so that we can see disasm of the code
# that would run in the release mode.
runtime_checks = []
stats_allocator = []
Loading