summaryrefslogtreecommitdiff
diff options
-rw-r--r--lib/prism/ffi.rb12
-rw-r--r--prism/extension.c5
-rw-r--r--prism/templates/lib/prism/serialize.rb.erb2
-rw-r--r--test/prism/ractor_test.rb61
4 files changed, 76 insertions, 4 deletions
diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb
index 35b91e41b2..a0da0b6195 100644
--- a/lib/prism/ffi.rb
+++ b/lib/prism/ffi.rb
@@ -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.
diff --git a/prism/extension.c b/prism/extension.c
index dca2ee67a1..1533ca7bb3 100644
--- a/prism/extension.c
+++ b/prism/extension.c
@@ -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");
diff --git a/prism/templates/lib/prism/serialize.rb.erb b/prism/templates/lib/prism/serialize.rb.erb
index 52821e0f7d..bc32e2fd4e 100644
--- a/prism/templates/lib/prism/serialize.rb.erb
+++ b/prism/templates/lib/prism/serialize.rb.erb
@@ -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
diff --git a/test/prism/ractor_test.rb b/test/prism/ractor_test.rb
new file mode 100644
index 0000000000..e55a9e8c47
--- /dev/null
+++ b/test/prism/ractor_test.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+return unless defined?(Ractor)
+
+require_relative "test_helper"
+
+module Prism
+ class RactorTest < TestCase
+ def test_version
+ refute_nil(with_ractor { Prism::VERSION })
+ end
+
+ def test_parse_file
+ assert_kind_of(Prism::Result, with_ractor(__FILE__) { |filepath| Prism.parse_file(filepath) })
+ end
+
+ def test_lex_file
+ assert_kind_of(Prism::Result, with_ractor(__FILE__) { |filepath| Prism.lex_file(filepath) })
+ end
+
+ def test_parse_file_comments
+ assert_kind_of(Array, with_ractor(__FILE__) { |filepath| Prism.parse_file_comments(filepath) })
+ end
+
+ def test_parse_lex_file
+ assert_kind_of(Prism::Result, with_ractor(__FILE__) { |filepath| Prism.parse_lex_file(filepath) })
+ end
+
+ def test_parse_success
+ assert(with_ractor("1 + 1") { |source| Prism.parse_success?(source) })
+ end
+
+ def test_parse_failure
+ assert(with_ractor("1 +") { |source| Prism.parse_failure?(source) })
+ end
+
+ def test_string_query_local
+ assert(with_ractor("foo") { |source| StringQuery.local?(source) })
+ end
+
+ def test_string_query_constant
+ assert(with_ractor("FOO") { |source| StringQuery.constant?(source) })
+ end
+
+ def test_string_query_method_name
+ assert(with_ractor("foo?") { |source| StringQuery.method_name?(source) })
+ end
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ def test_dump_file
+ assert_kind_of(String, with_ractor(__FILE__) { |filepath| Prism.dump_file(filepath) })
+ end
+ end
+
+ private
+
+ def with_ractor(*arguments, &block)
+ ignore_warnings { Ractor.new(*arguments, &block) }.take
+ end
+ end
+end