[ruby/prism] Mark Prism as ractor-safe

https://github.com/ruby/prism/commit/c02429765b
This commit is contained in:
Kevin Newton 2025-03-19 16:50:41 -04:00 committed by git
parent 050ffab82b
commit 641f15b1c6
6 changed files with 98 additions and 5 deletions

View File

@ -7,6 +7,10 @@
require "rbconfig"
require "ffi"
# We want to eagerly load this file if there are Ractors so that it does not get
# autoloaded from within a non-main Ractor.
require "prism/serialize" if defined?(Ractor)
module Prism
module LibRubyParser # :nodoc:
extend FFI::Library
@ -159,6 +163,9 @@ module Prism
class PrismString # :nodoc:
SIZEOF = LibRubyParser.pm_string_sizeof
PLATFORM_EXPECTS_UTF8 =
RbConfig::CONFIG["host_os"].match?(/bccwin|cygwin|djgpp|mingw|mswin|wince|darwin/i)
attr_reader :pointer, :length
def initialize(pointer, length, from_string)
@ -193,8 +200,7 @@ module Prism
# On Windows and Mac, it's expected that filepaths will be encoded in
# UTF-8. If they are not, we need to convert them to UTF-8 before
# passing them into pm_string_mapped_init.
if RbConfig::CONFIG["host_os"].match?(/bccwin|cygwin|djgpp|mingw|mswin|wince|darwin/i) &&
(encoding = filepath.encoding) != Encoding::ASCII_8BIT && encoding != Encoding::UTF_8
if PLATFORM_EXPECTS_UTF8 && (encoding = filepath.encoding) != Encoding::ASCII_8BIT && encoding != Encoding::UTF_8
filepath = filepath.encode(Encoding::UTF_8)
end
@ -223,7 +229,7 @@ module Prism
private_constant :LibRubyParser
# The version constant is set by reading the result of calling pm_version.
VERSION = LibRubyParser.pm_version.read_string
VERSION = LibRubyParser.pm_version.read_string.freeze
class << self
# Mirror the Prism.dump API by using the serialization API.

View File

@ -1331,6 +1331,11 @@ Init_prism(void) {
);
}
#ifdef HAVE_RB_EXT_RACTOR_SAFE
// Mark this extension as Ractor-safe.
rb_ext_ractor_safe(true);
#endif
// Grab up references to all of the constants that we're going to need to
// reference throughout this extension.
rb_cPrism = rb_define_module("Prism");

View File

@ -592,7 +592,7 @@ module Prism
<%- tokens.each do |token| -%>
<%= token.name.to_sym.inspect %>,
<%- end -%>
]
].freeze
private_constant :MAJOR_VERSION, :MINOR_VERSION, :PATCH_VERSION
private_constant :ConstantPool, :FastStringIO, :Loader, :TOKEN_TYPES

77
test/prism/ractor_test.rb Normal file
View File

@ -0,0 +1,77 @@
# frozen_string_literal: true
return unless defined?(Ractor)
require_relative "test_helper"
return if Prism::TestCase.windows?
module Prism
class RactorTest < TestCase
def test_version
assert_match(/\A\d+\.\d+\.\d+\z/, with_ractor { Prism::VERSION })
end
def test_parse_file
assert_equal("Prism::ParseResult", with_ractor(__FILE__) { |filepath| Prism.parse_file(filepath).class })
end
def test_lex_file
assert_equal("Prism::LexResult", with_ractor(__FILE__) { |filepath| Prism.lex_file(filepath).class })
end
def test_parse_file_comments
assert_equal("Array", with_ractor(__FILE__) { |filepath| Prism.parse_file_comments(filepath).class })
end
def test_parse_lex_file
assert_equal("Prism::ParseLexResult", with_ractor(__FILE__) { |filepath| Prism.parse_lex_file(filepath).class })
end
def test_parse_success
assert_equal("true", with_ractor("1 + 1") { |source| Prism.parse_success?(source) })
end
def test_parse_failure
assert_equal("true", with_ractor("1 +") { |source| Prism.parse_failure?(source) })
end
def test_string_query_local
assert_equal("true", with_ractor("foo") { |source| StringQuery.local?(source) })
end
def test_string_query_constant
assert_equal("true", with_ractor("FOO") { |source| StringQuery.constant?(source) })
end
def test_string_query_method_name
assert_equal("true", with_ractor("foo?") { |source| StringQuery.method_name?(source) })
end
if !ENV["PRISM_BUILD_MINIMAL"]
def test_dump_file
result = with_ractor(__FILE__) { |filepath| Prism.dump_file(filepath) }
assert_operator(result, :start_with?, "PRISM")
end
end
private
# Note that this must be done in a subprocess, otherwise it can mess up
# CRuby's test suite.
def with_ractor(*arguments, &block)
reader, writer = IO.pipe
pid = fork do
reader.close
writer.puts(ignore_warnings { Ractor.new(*arguments, &block) }.take)
end
writer.close
result = reader.gets.chomp
Process.wait(pid)
result
end
end
end

View File

@ -339,7 +339,7 @@ module Prism
assert_warning("tap { redo; foo }", "statement not reached")
end
if RbConfig::CONFIG["host_os"].match?(/bccwin|cygwin|djgpp|mingw|mswin|wince/i)
if windows?
def test_shebang_ending_with_carriage_return
refute_warning("#!ruby\r\np(123)\n", compare: false)
end

View File

@ -212,6 +212,11 @@ module Prism
yield Encoding::EUC_TW, codepoints_euc_tw
end
# True if the current platform is Windows.
def self.windows?
RbConfig::CONFIG["host_os"].match?(/bccwin|cygwin|djgpp|mingw|mswin|wince/i)
end
private
if RUBY_ENGINE == "ruby" && RubyVM::InstructionSequence.compile("").to_a[4][:parser] != :prism