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 ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,19 @@ rest_arg(VALUE ast_value, const NODE *rest_arg)
return NODE_NAMED_REST_P(rest_arg) ? NEW_CHILD(ast_value, rest_arg) : no_name_rest();
}

static ID
node_colon_name(const NODE *node)
{
switch (nd_type(node)) {
case NODE_COLON2:
return RNODE_COLON2(node)->nd_mid;
case NODE_COLON3:
return RNODE_COLON3(node)->nd_mid;
default:
rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
}
}

static VALUE
node_children(VALUE ast_value, const NODE *node)
{
Expand Down Expand Up @@ -497,7 +510,7 @@ node_children(VALUE ast_value, const NODE *node)
if (RNODE_CDECL(node)->nd_vid) {
return rb_ary_new_from_args(2, ID2SYM(RNODE_CDECL(node)->nd_vid), NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_value));
}
return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_else), ID2SYM(RNODE_COLON2(RNODE_CDECL(node)->nd_else)->nd_mid), NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_value));
return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_else), ID2SYM(node_colon_name(RNODE_CDECL(node)->nd_else)), NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_value));
case NODE_OP_ASGN1:
return rb_ary_new_from_args(4, NEW_CHILD(ast_value, RNODE_OP_ASGN1(node)->nd_recv),
ID2SYM(RNODE_OP_ASGN1(node)->nd_mid),
Expand Down
46 changes: 39 additions & 7 deletions prism_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -11458,6 +11458,37 @@ pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t
return lines;
}

struct load_from_fd_args {
VALUE path;
VALUE io;
int open_mode;
int fd;
};

static VALUE
close_file(VALUE args)
{
struct load_from_fd_args *arg = (void *)args;
if (arg->fd != -1) {
close(arg->fd);
}
else if (!NIL_P(arg->io)) {
rb_io_close(arg->io);
}
return Qnil;
}

static VALUE
load_content(VALUE args)
{
struct load_from_fd_args *arg = (void *)args;
VALUE io = rb_io_fdopen(arg->fd, arg->open_mode, RSTRING_PTR(arg->path));
arg->io = io;
arg->fd = -1;
rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
return rb_funcall(io, rb_intern("read"), 0);
}

/**
* Attempt to load the file into memory. Return a Ruby error if the file cannot
* be read.
Expand All @@ -11478,13 +11509,14 @@ pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
// For non-regular files (pipes, character devices), we need to read
// through Ruby IO to properly release the GVL while waiting for data.
if (init_result == PM_SOURCE_INIT_ERROR_NON_REGULAR) {
const int open_mode = O_RDONLY | O_NONBLOCK;
int fd = open(RSTRING_PTR(filepath), open_mode);
if (fd == -1) goto error_generic;

VALUE io = rb_io_fdopen(fd, open_mode, RSTRING_PTR(filepath));
rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
VALUE contents = rb_funcall(io, rb_intern("read"), 0);
struct load_from_fd_args args = {
.path = filepath,
.open_mode = O_RDONLY | O_NONBLOCK,
.fd = rb_cloexec_open(RSTRING_PTR(filepath), args.open_mode, 0),
.io = Qnil,
};
if (args.fd == -1) goto error_generic;
VALUE contents = rb_ensure(load_content, (VALUE)&args, close_file, (VALUE)&args);

if (!RB_TYPE_P(contents, T_STRING)) goto error_generic;

Expand Down
11 changes: 11 additions & 0 deletions test/ruby/test_ast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,17 @@ def test_parse_file_raises_syntax_error
end
end

def test_cdecl_children_with_toplevel_constant_path
# [Bug #21974]
children = parse("::Foo = 1").children[2].children

assert_equal(:COLON3, children[0].type)
assert_equal([:Foo], children[0].children)
assert_equal(:Foo, children[1])
assert_equal(:INTEGER, children[2].type)
assert_equal([1], children[2].children)
end

def assert_parse(code, warning: '')
node = assert_warning(warning) {RubyVM::AbstractSyntaxTree.parse(code)}
assert_kind_of(RubyVM::AbstractSyntaxTree::Node, node, code)
Expand Down
6 changes: 6 additions & 0 deletions test/ruby/test_compile_prism.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2721,6 +2721,12 @@ def test_parse_file
assert_raise TypeError do
RubyVM::InstructionSequence.compile_file_prism(nil)
end

assert_nothing_raised(Errno::EMFILE, Errno::ENFILE) do
10000.times do
RubyVM::InstructionSequence.compile_file_prism(File::NULL)
end
end
end

private
Expand Down