[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
parent dcc976add9
commit f8355e88d6
5 changed files with 69 additions and 19 deletions

4
iseq.c
View File

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

2
load.c
View File

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

View File

@ -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 * Attempt to load the file into memory. Return a Ruby error if the file cannot
* parse result struct. It returns a Ruby error if the file cannot be read or * be read.
* 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.
*/ */
VALUE 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))) { if (!pm_string_mapped_init(&result->input, RSTRING_PTR(filepath))) {
#ifdef _WIN32 #ifdef _WIN32
@ -8274,6 +8270,18 @@ pm_parse_file(pm_parse_result_t *result, VALUE filepath)
return err; 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); VALUE error = pm_parse_input(result, filepath);
// If we're parsing a filepath, then we need to potentially support the // 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; 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 * 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 * resulting scope node in the given parse result struct. This function could

View File

@ -44,7 +44,9 @@ typedef struct {
bool parsed; bool parsed;
} pm_parse_result_t; } 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_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); VALUE pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath);
void pm_parse_result_free(pm_parse_result_t *result); void pm_parse_result_free(pm_parse_result_t *result);

43
ruby.c
View File

@ -2080,11 +2080,15 @@ process_script(ruby_cmdline_options_t *opt)
return ast; 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 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); ruby_opt_init(opt);
memset(result, 0, sizeof(pm_parse_result_t));
if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) { if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
rb_category_warn( rb_category_warn(
@ -2095,8 +2099,18 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
"issue tracker." "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_t *options = &result->options;
pm_options_line_set(options, 1); pm_options_line_set(options, 1);
pm_options_command_line_p_set(options, opt->do_print); pm_options_command_line_p_set(options, opt->do_print);
pm_options_command_line_n_set(options, opt->do_loop); 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"); rb_raise(rb_eRuntimeError, "Prism support for streaming code from stdin is not currently supported");
} }
else if (opt->e_script) { else if (opt->e_script) {
prism_opt_init(opt);
error = pm_parse_string(result, opt->e_script, rb_str_new2("-e")); error = pm_parse_string(result, opt->e_script, rb_str_new2("-e"));
} }
else { 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 // If reading the file did not error, at that point we load the command
// global DATA constant that is a file object that can be read // line options. We do it in this order so that if the main script fails
// to read the contents after the marker. // 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) { if (NIL_P(error) && result->parser.data_loc.start != NULL) {
int xflag = opt->xflag; int xflag = opt->xflag;
VALUE file = open_load_file(opt->script_name, &xflag); VALUE file = open_load_file(opt->script_name, &xflag);
size_t offset = result->parser.data_loc.start - result->parser.start + 7; const pm_parser_t *parser = &result->parser;
if ((result->parser.start + offset < result->parser.end) && result->parser.start[offset] == '\r') offset++; size_t offset = parser->data_loc.start - parser->start + 7;
if ((result->parser.start + offset < result->parser.end) && result->parser.start[offset] == '\n') offset++;
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_funcall(file, rb_intern_const("seek"), 2, SIZET2NUM(offset), INT2FIX(SEEK_SET));
rb_define_global_const("DATA", file); rb_define_global_const("DATA", file);