[PRISM] Do not load -r until we check if main script can be read

This commit is contained in:
Kevin Newton 2024-02-28 12:12:45 -05:00
Родитель dcc976add9
Коммит f8355e88d6
5 изменённых файлов: 69 добавлений и 19 удалений

4
iseq.c
Просмотреть файл

@ -1245,7 +1245,7 @@ pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V
VALUE error;
if (RB_TYPE_P(src, T_FILE)) {
VALUE filepath = rb_io_path(src);
error = pm_parse_file(&result, filepath);
error = pm_load_parse_file(&result, filepath);
RB_GC_GUARD(filepath);
}
else {
@ -1646,7 +1646,7 @@ iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self)
pm_parse_result_t result = { 0 };
result.options.line = 1;
VALUE error = pm_parse_file(&result, file);
VALUE error = pm_load_parse_file(&result, file);
if (error == Qnil) {
make_compile_option(&option, opt);

2
load.c
Просмотреть файл

@ -747,7 +747,7 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
pm_parse_result_t result = { 0 };
result.options.line = 1;
VALUE error = pm_parse_file(&result, fname);
VALUE error = pm_load_parse_file(&result, fname);
if (error == Qnil) {
iseq = pm_iseq_new_top(&result.node, rb_fstring_lit("<top (required)>"), fname, realpath_internal_cached(realpath_map, fname), NULL);

Просмотреть файл

@ -8252,15 +8252,11 @@ pm_parse_file_script_lines(const pm_parser_t *parser)
}
/**
* Parse the given filepath and store the resulting scope node in the given
* parse result struct. It returns a Ruby error if the file cannot be read or
* if it cannot be parsed properly. It is assumed that the parse result object
* is zeroed out.
*
* TODO: This should raise a better error when the file cannot be read.
* Attempt to load the file into memory. Return a Ruby error if the file cannot
* be read.
*/
VALUE
pm_parse_file(pm_parse_result_t *result, VALUE filepath)
pm_load_file(pm_parse_result_t *result, VALUE filepath)
{
if (!pm_string_mapped_init(&result->input, RSTRING_PTR(filepath))) {
#ifdef _WIN32
@ -8274,6 +8270,18 @@ pm_parse_file(pm_parse_result_t *result, VALUE filepath)
return err;
}
return Qnil;
}
/**
* Parse the given filepath and store the resulting scope node in the given
* parse result struct. It returns a Ruby error if the file cannot be read or
* if it cannot be parsed properly. It is assumed that the parse result object
* is zeroed out.
*/
VALUE
pm_parse_file(pm_parse_result_t *result, VALUE filepath)
{
VALUE error = pm_parse_input(result, filepath);
// If we're parsing a filepath, then we need to potentially support the
@ -8292,6 +8300,21 @@ pm_parse_file(pm_parse_result_t *result, VALUE filepath)
return error;
}
/**
* Load and then parse the given filepath. It returns a Ruby error if the file
* cannot be read or if it cannot be parsed properly.
*/
VALUE
pm_load_parse_file(pm_parse_result_t *result, VALUE filepath)
{
VALUE error = pm_load_file(result, filepath);
if (NIL_P(error)) {
error = pm_parse_file(result, filepath);
}
return error;
}
/**
* Parse the given source that corresponds to the given filepath and store the
* resulting scope node in the given parse result struct. This function could

Просмотреть файл

@ -44,7 +44,9 @@ typedef struct {
bool parsed;
} pm_parse_result_t;
VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath);
VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath);
VALUE pm_load_parse_file(pm_parse_result_t *result, VALUE filepath);
VALUE pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath);
void pm_parse_result_free(pm_parse_result_t *result);

43
ruby.c
Просмотреть файл

@ -2080,11 +2080,15 @@ process_script(ruby_cmdline_options_t *opt)
return ast;
}
/**
* Call ruby_opt_init to set up the global state based on the command line
* options, and then warn if prism is enabled and the experimental warning
* category is enabled.
*/
static void
prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
prism_opt_init(ruby_cmdline_options_t *opt)
{
ruby_opt_init(opt);
memset(result, 0, sizeof(pm_parse_result_t));
if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
rb_category_warn(
@ -2095,8 +2099,18 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
"issue tracker."
);
}
}
/**
* Process the command line options and parse the script into the given result.
* Raise an error if the script cannot be parsed.
*/
static void
prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
{
memset(result, 0, sizeof(pm_parse_result_t));
pm_options_t *options = &result->options;
pm_options_line_set(options, 1);
pm_options_command_line_p_set(options, opt->do_print);
pm_options_command_line_n_set(options, opt->do_loop);
@ -2108,21 +2122,32 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
rb_raise(rb_eRuntimeError, "Prism support for streaming code from stdin is not currently supported");
}
else if (opt->e_script) {
prism_opt_init(opt);
error = pm_parse_string(result, opt->e_script, rb_str_new2("-e"));
}
else {
error = pm_parse_file(result, opt->script_name);
error = pm_load_file(result, opt->script_name);
// If we found an __END__ marker, then we're going to define a
// global DATA constant that is a file object that can be read
// to read the contents after the marker.
// If reading the file did not error, at that point we load the command
// line options. We do it in this order so that if the main script fails
// to load, it doesn't require files required by -r.
if (NIL_P(error)) {
prism_opt_init(opt);
error = pm_parse_file(result, opt->script_name);
}
// If we found an __END__ marker, then we're going to define a global
// DATA constant that is a file object that can be read to read the
// contents after the marker.
if (NIL_P(error) && result->parser.data_loc.start != NULL) {
int xflag = opt->xflag;
VALUE file = open_load_file(opt->script_name, &xflag);
size_t offset = result->parser.data_loc.start - result->parser.start + 7;
if ((result->parser.start + offset < result->parser.end) && result->parser.start[offset] == '\r') offset++;
if ((result->parser.start + offset < result->parser.end) && result->parser.start[offset] == '\n') offset++;
const pm_parser_t *parser = &result->parser;
size_t offset = parser->data_loc.start - parser->start + 7;
if ((parser->start + offset < parser->end) && parser->start[offset] == '\r') offset++;
if ((parser->start + offset < parser->end) && parser->start[offset] == '\n') offset++;
rb_funcall(file, rb_intern_const("seek"), 2, SIZET2NUM(offset), INT2FIX(SEEK_SET));
rb_define_global_const("DATA", file);