summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Muta <andremuta@gmail.com>2025-10-24 15:04:04 -0300
committerKevin Newton <kddnewton@gmail.com>2025-10-30 09:17:58 -0400
commit244a37bd53fb5c7ba219ef958b6adaa2c4b0b67e (patch)
tree6224d5bc8ed137d2ed73a9e3fddf481d1aab57f7
parent8e7d427a721ab715361c577fdea94fdebece6c6d (diff)
Fixes [Bug #21522] eval isolation in Ractors for Prism
-rw-r--r--bootstraptest/test_ractor.rb31
-rw-r--r--vm_eval.c42
2 files changed, 52 insertions, 21 deletions
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb
index 65ef07fb73..fef55ffd68 100644
--- a/bootstraptest/test_ractor.rb
+++ b/bootstraptest/test_ractor.rb
@@ -754,6 +754,37 @@ assert_equal 'ArgumentError', %q{
end
}
+# eval with outer locals in a Ractor raises SyntaxError
+# [Bug #21522]
+assert_equal 'SyntaxError', %q{
+ outer = 42
+ r = Ractor.new do
+ eval("outer")
+ end
+ begin
+ r.value
+ rescue Ractor::RemoteError => e
+ e.cause.class
+ end
+}
+
+# eval of an undefined name in a Ractor raises NameError
+assert_equal 'NameError', %q{
+ r = Ractor.new do
+ eval("totally_undefined_name")
+ end
+ begin
+ r.value
+ rescue Ractor::RemoteError => e
+ e.cause.class
+ end
+}
+
+# eval of a local defined inside the Ractor works
+assert_equal '99', %q{
+ Ractor.new { inner = 99; eval("inner").to_s }.value
+}
+
# ivar in shareable-objects are not allowed to access from non-main Ractor
assert_equal "can not get unshareable values from instance variables of classes/modules from non-main Ractors", <<~'RUBY', frozen_string_literal: false
class C
diff --git a/vm_eval.c b/vm_eval.c
index 71656f5a0f..b791cd4990 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1669,6 +1669,24 @@ get_eval_default_path(void)
return eval_default_path;
}
+static inline int
+compute_isolated_depth_from_ep(const VALUE *ep)
+{
+ int depth = 1;
+ while (1) {
+ if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ISOLATED)) return depth;
+ if (VM_ENV_LOCAL_P(ep)) return 0;
+ ep = VM_ENV_PREV_EP(ep);
+ depth++;
+ }
+}
+
+static inline int
+compute_isolated_depth_from_block(const struct rb_block *blk)
+{
+ return compute_isolated_depth_from_ep(vm_block_ep(blk));
+}
+
static const rb_iseq_t *
pm_eval_make_iseq(VALUE src, VALUE fname, int line,
const struct rb_block *base_block)
@@ -1677,8 +1695,8 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
const rb_iseq_t *iseq = parent;
VALUE name = rb_fstring_lit("<compiled>");
- // Conditionally enable coverage depending on the current mode:
int coverage_enabled = ((rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0) ? 1 : 0;
+ int isolated_depth = compute_isolated_depth_from_block(base_block);
if (!fname) {
fname = rb_source_location(&line);
@@ -1872,7 +1890,7 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
#undef FORWARDING_ALL_STR
int error_state;
- iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, 0, &error_state);
+ iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, isolated_depth, &error_state);
pm_scope_node_t *prev = result.node.previous;
while (prev) {
@@ -1908,27 +1926,9 @@ eval_make_iseq(VALUE src, VALUE fname, int line,
rb_iseq_t *iseq = NULL;
VALUE ast_value;
rb_ast_t *ast;
- int isolated_depth = 0;
- // Conditionally enable coverage depending on the current mode:
int coverage_enabled = (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0;
-
- {
- int depth = 1;
- const VALUE *ep = vm_block_ep(base_block);
-
- while (1) {
- if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ISOLATED)) {
- isolated_depth = depth;
- break;
- }
- else if (VM_ENV_LOCAL_P(ep)) {
- break;
- }
- ep = VM_ENV_PREV_EP(ep);
- depth++;
- }
- }
+ int isolated_depth = compute_isolated_depth_from_block(base_block);
if (!fname) {
fname = rb_source_location(&line);