summaryrefslogtreecommitdiff
path: root/ruby.c
diff options
context:
space:
mode:
Diffstat (limited to 'ruby.c')
-rw-r--r--ruby.c517
1 files changed, 339 insertions, 178 deletions
diff --git a/ruby.c b/ruby.c
index bfff15ba30..162287ca71 100644
--- a/ruby.c
+++ b/ruby.c
@@ -105,9 +105,9 @@ void rb_warning_category_update(unsigned int mask, unsigned int bits);
SEP \
X(frozen_string_literal) \
SEP \
- X(rjit) \
- SEP \
X(yjit) \
+ SEP \
+ X(zjit) \
/* END OF FEATURES */
#define EACH_DEBUG_FEATURES(X, SEP) \
X(frozen_string_literal) \
@@ -117,13 +117,14 @@ void rb_warning_category_update(unsigned int mask, unsigned int bits);
#define DEFINE_DEBUG_FEATURE(bit) feature_debug_##bit
enum feature_flag_bits {
EACH_FEATURES(DEFINE_FEATURE, COMMA),
+ DEFINE_FEATURE(frozen_string_literal_set),
feature_debug_flag_first,
-#if defined(RJIT_FORCE_ENABLE) || !USE_YJIT
- DEFINE_FEATURE(jit) = feature_rjit,
+#if !USE_YJIT && USE_ZJIT
+ DEFINE_FEATURE(jit) = feature_zjit,
#else
DEFINE_FEATURE(jit) = feature_yjit,
#endif
- feature_jit_mask = FEATURE_BIT(rjit) | FEATURE_BIT(yjit),
+ feature_jit_mask = FEATURE_BIT(yjit) | FEATURE_BIT(zjit),
feature_debug_flag_begin = feature_debug_flag_first - 1,
EACH_DEBUG_FEATURES(DEFINE_DEBUG_FEATURE, COMMA),
@@ -189,6 +190,7 @@ enum {
COMPILATION_FEATURES = (
0
| FEATURE_BIT(frozen_string_literal)
+ | FEATURE_BIT(frozen_string_literal_set)
| FEATURE_BIT(debug_frozen_string_literal)
),
DEFAULT_FEATURES = (
@@ -197,6 +199,7 @@ enum {
& ~FEATURE_BIT(gems)
#endif
& ~FEATURE_BIT(frozen_string_literal)
+ & ~FEATURE_BIT(frozen_string_literal_set)
& ~feature_jit_mask
)
};
@@ -214,9 +217,7 @@ cmdline_options_init(ruby_cmdline_options_t *opt)
opt->ext.enc.index = -1;
opt->intern.enc.index = -1;
opt->features.set = DEFAULT_FEATURES;
-#ifdef RJIT_FORCE_ENABLE /* to use with: ./configure cppflags="-DRJIT_FORCE_ENABLE" */
- opt->features.set |= FEATURE_BIT(rjit);
-#elif defined(YJIT_FORCE_ENABLE)
+#if defined(YJIT_FORCE_ENABLE)
opt->features.set |= FEATURE_BIT(yjit);
#endif
opt->dump |= DUMP_BIT(opt_optimize);
@@ -304,15 +305,17 @@ ruby_show_usage_line(const char *name, const char *secondary, const char *descri
description, help, highlight, width, columns);
}
+RUBY_EXTERN const char ruby_api_version_name[];
+
static void
usage(const char *name, int help, int highlight, int columns)
{
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
#if USE_YJIT
-# define PLATFORM_JIT_OPTION "--yjit"
-#else
-# define PLATFORM_JIT_OPTION "--rjit (experimental)"
+# define DEFAULT_JIT_OPTION "--yjit"
+#elif USE_ZJIT
+# define DEFAULT_JIT_OPTION "--zjit"
#endif
/* This message really ought to be max 23 lines.
@@ -323,14 +326,14 @@ usage(const char *name, int help, int highlight, int columns)
M("-a", "", "Split each input line ($_) into fields ($F)."),
M("-c", "", "Check syntax (no execution)."),
M("-Cdirpath", "", "Execute program in specified directory."),
- M("-d", ", --debug", "Set debugging flag ($DEBUG) to true."),
+ M("-d", ", --debug", "Set debugging flag ($DEBUG) and $VERBOSE to true."),
M("-e 'code'", "", "Execute given Ruby code; multiple -e allowed."),
M("-Eex[:in]", ", --encoding=ex[:in]", "Set default external and internal encodings."),
M("-Fpattern", "", "Set input field separator ($;); used with -a."),
M("-i[extension]", "", "Set ARGF in-place mode;\n"
"create backup files with given extension."),
- M("-Idirpath", "", "Add specified directory to load paths ($LOAD_PATH);\n"
- "multiple -I allowed."),
+ M("-Idirpath", "", "Prepend specified directory to load paths ($LOAD_PATH);\n"
+ "relative paths are expanded; multiple -I are allowed."),
M("-l", "", "Set output record separator ($\\) to $/;\n"
"used for line-oriented output."),
M("-n", "", "Run program in gets loop."),
@@ -343,16 +346,18 @@ usage(const char *name, int help, int highlight, int columns)
M("-W[level=2|:category]", "", "Set warning flag ($-W):\n"
"0 for silent; 1 for moderate; 2 for verbose."),
M("-x[dirpath]", "", "Execute Ruby code starting from a #!ruby line."),
- M("--jit", "", "Enable JIT for the platform; same as " PLATFORM_JIT_OPTION "."),
+#if USE_YJIT || USE_ZJIT
+ M("--jit", "", "Enable the default JIT for the build; same as " DEFAULT_JIT_OPTION "."),
+#endif
#if USE_YJIT
M("--yjit", "", "Enable in-process JIT compiler."),
#endif
-#if USE_RJIT
- M("--rjit", "", "Enable pure-Ruby JIT compiler (experimental)."),
+#if USE_ZJIT
+ M("--zjit", "", "Enable method-based JIT compiler."),
#endif
M("-h", "", "Print this help message; use --help for longer message."),
};
- STATIC_ASSERT(usage_msg_size, numberof(usage_msg) < 25);
+ STATIC_ASSERT(usage_msg_size, numberof(usage_msg) < 26);
static const struct ruby_opt_message help_msg[] = {
M("--backtrace-limit=num", "", "Set backtrace limit."),
@@ -375,7 +380,7 @@ usage(const char *name, int help, int highlight, int columns)
M("parsetree", "", "Abstract syntax tree (AST)."),
M("-optimize", "", "Disable optimization (affects insns)."),
M("+error-tolerant", "", "Error-tolerant parsing (affects yydebug, parsetree)."),
- M("+comment", "", "Add comments to AST (affects parsetree)."),
+ M("+comment", "", "Add comments to AST (affects parsetree with --parser=parse.y)."),
};
static const struct ruby_opt_message features[] = {
M("gems", "", "Rubygems (only for debugging, default: "DEFAULT_RUBYGEMS_ENABLED")."),
@@ -387,18 +392,16 @@ usage(const char *name, int help, int highlight, int columns)
#if USE_YJIT
M("yjit", "", "In-process JIT compiler (default: disabled)."),
#endif
-#if USE_RJIT
- M("rjit", "", "Pure-Ruby JIT compiler (experimental, default: disabled)."),
+#if USE_ZJIT
+ M("zjit", "", "Method-based JIT compiler (default: disabled)."),
#endif
};
static const struct ruby_opt_message warn_categories[] = {
M("deprecated", "", "Deprecated features."),
M("experimental", "", "Experimental features."),
M("performance", "", "Performance issues."),
+ M("strict_unused_block", "", "Warning unused block strictly"),
};
-#if USE_RJIT
- extern const struct ruby_opt_message rb_rjit_option_messages[];
-#endif
int i;
const char *sb = highlight ? esc_standout+1 : esc_none;
const char *se = highlight ? esc_reset : esc_none;
@@ -429,10 +432,10 @@ usage(const char *name, int help, int highlight, int columns)
printf("%s""YJIT options:%s\n", sb, se);
rb_yjit_show_usage(help, highlight, w, columns);
#endif
-#if USE_RJIT
- printf("%s""RJIT options (experimental):%s\n", sb, se);
- for (i = 0; rb_rjit_option_messages[i].str; ++i)
- SHOW(rb_rjit_option_messages[i]);
+#if USE_ZJIT
+ printf("%s""ZJIT options:%s\n", sb, se);
+ extern void rb_zjit_show_usage(int help, int highlight, unsigned int width, int columns);
+ rb_zjit_show_usage(help, highlight, w, columns);
#endif
}
@@ -443,7 +446,7 @@ ruby_push_include(const char *path, VALUE (*filter)(VALUE))
{
const char sep = PATH_SEP_CHAR;
const char *p, *s;
- VALUE load_path = GET_VM()->load_path;
+ VALUE load_path = rb_root_box()->load_path;
#ifdef __CYGWIN__
char rubylib[FILENAME_MAX];
VALUE buf = 0;
@@ -556,6 +559,8 @@ translit_char_bin(char *p, int from, int to)
#endif
#ifdef _WIN32
+# undef chdir
+# define chdir rb_w32_uchdir
# define UTF8_PATH 1
#endif
@@ -746,7 +751,7 @@ ruby_init_loadpath(void)
rb_gc_register_address(&ruby_archlibdir_path);
ruby_archlibdir_path = archlibdir;
- load_path = GET_VM()->load_path;
+ load_path = rb_root_box()->load_path;
ruby_push_include(getenv("RUBYLIB"), identical_path);
@@ -758,8 +763,6 @@ ruby_init_loadpath(void)
rb_ary_push(load_path, path);
paths += len + 1;
}
-
- rb_const_set(rb_cObject, rb_intern_const("TMP_RUBY_PREFIX"), ruby_prefix_path);
}
@@ -795,6 +798,25 @@ require_libraries(VALUE *req_list)
*req_list = 0;
}
+static void
+require_libraries_in_main_box(VALUE *req_list)
+{
+ const rb_box_t *main_box = rb_main_box();
+ VALUE list = *req_list;
+ ID require;
+ rb_encoding *extenc = rb_default_external_encoding();
+
+ CONST_ID(require, "require");
+ while (list && RARRAY_LEN(list) > 0) {
+ VALUE feature = rb_ary_shift(list);
+ rb_enc_associate(feature, extenc);
+ RBASIC_SET_CLASS_RAW(feature, rb_cString);
+ OBJ_FREEZE(feature);
+ rb_funcallv(main_box->box_object, require, 1, &feature);
+ }
+ *req_list = 0;
+}
+
static const struct rb_block*
toplevel_context(rb_binding_t *bind)
{
@@ -910,7 +932,9 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
argc = RSTRING_LEN(argary) / sizeof(ap);
ap = 0;
rb_str_cat(argary, (char *)&ap, sizeof(ap));
- argv = ptr = ALLOC_N(char *, argc);
+
+ VALUE ptr_obj;
+ argv = ptr = RB_ALLOCV_N(char *, ptr_obj, argc);
MEMMOVE(argv, RSTRING_PTR(argary), char *, argc);
while ((i = proc_options(argc, argv, opt, envopt)) > 1 && envopt && (argc -= i) > 0) {
@@ -942,7 +966,8 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
opt->crash_report = crash_report;
}
- ruby_xfree(ptr);
+ RB_ALLOCV_END(ptr_obj);
+
/* get rid of GC */
rb_str_resize(argary, 0);
rb_str_resize(argstr, 0);
@@ -1005,7 +1030,7 @@ feature_option(const char *str, int len, void *arg, const unsigned int enable)
goto found;
}
if (NAME_MATCH_P("all", str, len)) {
- // YJIT and RJIT cannot be enabled at the same time. We enable only one for --enable=all.
+ // We enable only one JIT for --enable=all.
mask &= ~feature_jit_mask | FEATURE_BIT(jit);
goto found;
}
@@ -1033,6 +1058,9 @@ feature_option(const char *str, int len, void *arg, const unsigned int enable)
found:
FEATURE_SET_TO(*argp, mask, (mask & enable));
+ if (NAME_MATCH_P("frozen_string_literal", str, len)) {
+ FEATURE_SET_TO(*argp, FEATURE_BIT(frozen_string_literal_set), FEATURE_BIT(frozen_string_literal_set));
+ }
return;
}
@@ -1194,6 +1222,19 @@ setup_yjit_options(const char *s)
}
#endif
+#if USE_ZJIT
+static void
+setup_zjit_options(const char *s)
+{
+ // The option parsing is done in zjit/src/options.rs
+ extern bool rb_zjit_parse_option(const char *s);
+
+ if (!rb_zjit_parse_option(s)) {
+ rb_raise(rb_eRuntimeError, "invalid ZJIT option '%s' (--help will show valid zjit options)", s);
+ }
+}
+#endif
+
/*
* Following proc_*_option functions are tree kinds:
*
@@ -1225,6 +1266,9 @@ proc_W_option(ruby_cmdline_options_t *opt, const char *s, int *warning)
else if (NAME_MATCH_P("performance", s, len)) {
bits = 1U << RB_WARN_CATEGORY_PERFORMANCE;
}
+ else if (NAME_MATCH_P("strict_unused_block", s, len)) {
+ bits = 1U << RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK;
+ }
else {
rb_warn("unknown warning category: '%s'", s);
}
@@ -1335,11 +1379,11 @@ proc_0_option(ruby_cmdline_options_t *opt, const char *s)
if (v > 0377)
rb_rs = Qnil;
else if (v == 0 && numlen >= 2) {
- rb_rs = rb_str_new2("");
+ rb_rs = rb_fstring_lit("");
}
else {
c = v & 0xff;
- rb_rs = rb_str_new(&c, 1);
+ rb_rs = rb_str_freeze(rb_str_new(&c, 1));
}
return s;
}
@@ -1348,7 +1392,7 @@ proc_0_option(ruby_cmdline_options_t *opt, const char *s)
static void
proc_encoding_option(ruby_cmdline_options_t *opt, const char *s, const char *opt_name)
{
- char *p;
+ const char *p;
# define set_encoding_part(type) \
if (!(p = strchr(s, ':'))) { \
set_##type##_encoding_once(opt, s, 0); \
@@ -1422,10 +1466,10 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **
}
else if (is_option_with_arg("parser", Qfalse, Qtrue)) {
if (strcmp("prism", s) == 0) {
- (*rb_ruby_prism_ptr()) = true;
+ rb_ruby_default_parser_set(RB_DEFAULT_PARSER_PRISM);
}
else if (strcmp("parse.y", s) == 0) {
- // default behavior
+ rb_ruby_default_parser_set(RB_DEFAULT_PARSER_PARSE_Y);
}
else {
rb_raise(rb_eRuntimeError, "unknown parser %s", s);
@@ -1445,21 +1489,12 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **
ruby_verbose = Qtrue;
}
else if (strcmp("jit", s) == 0) {
-#if USE_YJIT || USE_RJIT
+#if USE_YJIT || USE_ZJIT
FEATURE_SET(opt->features, FEATURE_BIT(jit));
#else
rb_warn("Ruby was built without JIT support");
#endif
}
- else if (is_option_with_optarg("rjit", '-', true, false, false)) {
-#if USE_RJIT
- extern void rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt);
- FEATURE_SET(opt->features, FEATURE_BIT(rjit));
- rb_rjit_setup_options(s, &opt->rjit);
-#else
- rb_warn("RJIT support is disabled.");
-#endif
- }
else if (is_option_with_optarg("yjit", '-', true, false, false)) {
#if USE_YJIT
FEATURE_SET(opt->features, FEATURE_BIT(yjit));
@@ -1469,6 +1504,15 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **
" You may need to install rustc to build Ruby with YJIT.");
#endif
}
+ else if (is_option_with_optarg("zjit", '-', true, false, false)) {
+#if USE_ZJIT
+ FEATURE_SET(opt->features, FEATURE_BIT(zjit));
+ setup_zjit_options(s);
+#else
+ rb_warn("Ruby was built without ZJIT support."
+ " You may need to install rustc to build Ruby with ZJIT.");
+#endif
+ }
else if (strcmp("yydebug", s) == 0) {
if (envopt) goto noenvopt_long;
opt->dump |= DUMP_BIT(yydebug);
@@ -1709,11 +1753,27 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
if (!s[1])
break;
- default:
+ default: {
+ rb_encoding *enc = IF_UTF8_PATH(rb_utf8_encoding(), rb_locale_encoding());
+ const char *e = s + strlen(s);
+ int r = rb_enc_precise_mbclen(s, e, enc);
+ unsigned int c = (unsigned char)*s;
+ if (r > 0) {
+ c = rb_enc_mbc_to_codepoint(s, e, enc);
+ if (ONIGENC_IS_CODE_GRAPH(enc, c) ||
+ ((s = ruby_escaped_char(c)) != 0 &&
+ (r = (int)strlen(s), /* 3 at most */ 1))) {
+ rb_enc_raise(enc, rb_eRuntimeError,
+ "invalid option -%.*s (-h will show valid options)",
+ r, s);
+ }
+ }
rb_raise(rb_eRuntimeError,
- "invalid option -%c (-h will show valid options)",
- (int)(unsigned char)*s);
+ "invalid option -\\x%.2x (-h will show valid options)",
+ c);
+
goto switch_end;
+ }
noenvopt:
/* "EIdvwWrKU" only */
@@ -1730,13 +1790,13 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
return argc0 - argc;
}
+VALUE rb_define_gem_modules(VALUE, VALUE);
void Init_builtin_features(void);
static void
ruby_init_prelude(void)
{
Init_builtin_features();
- rb_const_remove(rb_cObject, rb_intern_const("TMP_RUBY_PREFIX"));
}
void rb_call_builtin_inits(void);
@@ -1758,41 +1818,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
if (opt->dump & dump_exit_bits) return;
- if (FEATURE_SET_P(opt->features, gems)) {
- rb_define_module("Gem");
- if (opt->features.set & FEATURE_BIT(error_highlight)) {
- rb_define_module("ErrorHighlight");
- }
- if (opt->features.set & FEATURE_BIT(did_you_mean)) {
- rb_define_module("DidYouMean");
- }
- if (opt->features.set & FEATURE_BIT(syntax_suggest)) {
- rb_define_module("SyntaxSuggest");
- }
- }
-
- /* [Feature #19785] Warning for removed GC environment variable.
- * Remove this in Ruby 3.4. */
- if (getenv("RUBY_GC_HEAP_INIT_SLOTS")) {
- rb_warn_deprecated("The environment variable RUBY_GC_HEAP_INIT_SLOTS",
- "environment variables RUBY_GC_HEAP_%d_INIT_SLOTS");
- }
-
- if (getenv("RUBY_FREE_AT_EXIT")) {
- rb_warn("Free at exit is experimental and may be unstable");
- rb_free_at_exit = true;
- }
-
-#if USE_RJIT
- // rb_call_builtin_inits depends on RubyVM::RJIT.enabled?
- if (opt->rjit.on)
- rb_rjit_enabled = true;
- if (opt->rjit.stats)
- rb_rjit_stats_enabled = true;
- if (opt->rjit.trace_exits)
- rb_rjit_trace_exits_enabled = true;
-#endif
-
Init_ext(); /* load statically linked extensions before rubygems */
Init_extra_exts();
@@ -1801,20 +1826,67 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
GET_VM()->running = 1;
memset(ruby_vm_redefined_flag, 0, sizeof(ruby_vm_redefined_flag));
+ // Register JIT-optimized builtin CMEs before the prelude, which may
+ // redefine core methods (e.g. Kernel.prepend via bundler/setup).
+#if USE_YJIT
+ rb_yjit_init_builtin_cmes();
+#endif
+#if USE_ZJIT
+ extern void rb_zjit_init_builtin_cmes(void);
+ rb_zjit_init_builtin_cmes();
+#endif
+
+ /**
+ * Initialize the root/main boxes before loading libraries to run them
+ * (including RubyGems, written in Ruby) in those boxes themselves
+ */
+ if (rb_box_available()) {
+ rb_initialize_mandatory_boxes();
+ }
+
+ rb_box_init_done();
+
+ if (FEATURE_SET_P(opt->features, gems)) {
+ rb_box_gem_flags_t gem_flags = {
+ .gem = FEATURE_SET_P(opt->features, gems),
+ .error_highlight = opt->features.set & FEATURE_BIT(error_highlight),
+ .did_you_mean = opt->features.set & FEATURE_BIT(did_you_mean),
+ .syntax_suggest = opt->features.set & FEATURE_BIT(syntax_suggest)
+ };
+
+ if (rb_box_available()) {
+ rb_vm_call_cfunc_in_box(Qnil, rb_define_gem_modules, (VALUE)&gem_flags, Qnil,
+ rb_str_new_cstr("before_prelude.root.dummy"), rb_root_box());
+ rb_vm_call_cfunc_in_box(Qnil, rb_define_gem_modules, (VALUE)&gem_flags, Qnil,
+ rb_str_new_cstr("before_prelude.main.dummy"), rb_main_box());
+
+ rb_box_set_gem_flags(&gem_flags);
+ }
+ else {
+ rb_define_gem_modules((VALUE)&gem_flags, Qnil);
+ }
+ }
+
+ // The root/main boxes load gem_prelude here.
+ // User boxes will load it in those #initialize instead.
ruby_init_prelude();
- // Initialize JITs after prelude because JITing prelude is typically not optimal.
-#if USE_RJIT
- // Also, rb_rjit_init is safe only after rb_call_builtin_inits() defines RubyVM::RJIT::Compiler.
- if (opt->rjit.on)
- rb_rjit_init(&opt->rjit);
-#endif
+ // Enable JITs after ruby_init_prelude() to avoid JITing prelude code.
#if USE_YJIT
rb_yjit_init(opt->yjit);
#endif
+#if USE_ZJIT
+ extern void rb_zjit_init(bool);
+ rb_zjit_init(opt->zjit);
+#endif
ruby_set_script_name(opt->script_name);
- require_libraries(&opt->req_list);
+ if (rb_box_available()) {
+ require_libraries_in_main_box(&opt->req_list);
+ }
+ else {
+ require_libraries(&opt->req_list);
+ }
}
static int
@@ -1977,7 +2049,7 @@ copy_str(VALUE str, rb_encoding *enc, bool intern)
return rb_enc_interned_str(RSTRING_PTR(str), RSTRING_LEN(str), enc);
}
-#if USE_YJIT
+#if USE_YJIT || USE_ZJIT
// Check that an environment variable is set to a truthy value
static bool
env_var_truthy(const char *name)
@@ -2060,7 +2132,7 @@ static VALUE
process_script(ruby_cmdline_options_t *opt)
{
rb_ast_t *ast;
- VALUE vast;
+ VALUE ast_value;
VALUE parser = rb_parser_new();
const unsigned int dump = opt->dump;
@@ -2080,7 +2152,7 @@ process_script(ruby_cmdline_options_t *opt)
ruby_set_script_name(progname);
rb_parser_set_options(parser, opt->do_print, opt->do_loop,
opt->do_line, opt->do_split);
- vast = rb_parser_compile_string(parser, opt->script, opt->e_script, 1);
+ ast_value = rb_parser_compile_string(parser, opt->script, opt->e_script, 1);
}
else {
VALUE f;
@@ -2088,34 +2160,56 @@ process_script(ruby_cmdline_options_t *opt)
f = open_load_file(opt->script_name, &xflag);
opt->xflag = xflag != 0;
rb_parser_set_context(parser, 0, f == rb_stdin);
- vast = load_file(parser, opt->script_name, f, 1, opt);
+ ast_value = load_file(parser, opt->script_name, f, 1, opt);
}
- ast = rb_ruby_ast_data_get(vast);
+ ast = rb_ruby_ast_data_get(ast_value);
if (!ast->body.root) {
rb_ast_dispose(ast);
return Qnil;
}
- return vast;
+ return ast_value;
+}
+
+static uint8_t
+prism_script_command_line(ruby_cmdline_options_t *opt)
+{
+ uint8_t command_line = 0;
+ if (opt->do_split) command_line |= PM_OPTIONS_COMMAND_LINE_A;
+ if (opt->do_line) command_line |= PM_OPTIONS_COMMAND_LINE_L;
+ if (opt->do_loop) command_line |= PM_OPTIONS_COMMAND_LINE_N;
+ if (opt->do_print) command_line |= PM_OPTIONS_COMMAND_LINE_P;
+ if (opt->xflag) command_line |= PM_OPTIONS_COMMAND_LINE_X;
+ return command_line;
}
-/**
- * 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_opt_init(ruby_cmdline_options_t *opt)
+prism_script_shebang_callback(pm_options_t *options, const uint8_t *source, size_t length, void *data)
{
- ruby_opt_init(opt);
+ ruby_cmdline_options_t *opt = (ruby_cmdline_options_t *) data;
+ opt->warning = 0;
+
+ char *switches = malloc(length + 1);
+ memcpy(switches, source, length);
+ switches[length] = '\0';
+
+ int no_src_enc = !opt->src.enc.name;
+ int no_ext_enc = !opt->ext.enc.name;
+ int no_int_enc = !opt->intern.enc.name;
- if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
- rb_category_warn(
- RB_WARN_CATEGORY_EXPERIMENTAL,
- "The compiler based on the Prism parser is currently experimental "
- "and compatibility with the compiler based on parse.y is not yet "
- "complete. Please report any issues you find on the `ruby/prism` "
- "issue tracker."
- );
+ moreswitches(switches, opt, 0);
+ free(switches);
+
+ pm_options_command_line_set(options, prism_script_command_line(opt));
+
+ if (no_src_enc && opt->src.enc.name) {
+ opt->src.enc.index = opt_enc_index(opt->src.enc.name);
+ pm_options_encoding_set(options, StringValueCStr(opt->ext.enc.name));
+ }
+ if (no_ext_enc && opt->ext.enc.name) {
+ opt->ext.enc.index = opt_enc_index(opt->ext.enc.name);
+ }
+ if (no_int_enc && opt->intern.enc.name) {
+ opt->intern.enc.index = opt_enc_index(opt->intern.enc.name);
}
}
@@ -2126,63 +2220,84 @@ prism_opt_init(ruby_cmdline_options_t *opt)
static void
prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
{
- memset(result, 0, sizeof(pm_parse_result_t));
+ pm_parse_result_init(result);
+ pm_options_t *options = result->options;
+ pm_options_main_script_set(options, true);
- pm_options_t *options = &result->options;
- pm_options_line_set(options, 1);
+ const bool read_stdin = (strcmp(opt->script, "-") == 0);
- if (opt->ext.enc.name != 0) {
- pm_options_encoding_set(options, StringValueCStr(opt->ext.enc.name));
+ if (read_stdin) {
+ pm_options_encoding_set(options, rb_enc_name(rb_locale_encoding()));
+ }
+ if (opt->src.enc.name != 0) {
+ pm_options_encoding_set(options, StringValueCStr(opt->src.enc.name));
}
- uint8_t command_line = 0;
- if (opt->do_split) command_line |= PM_OPTIONS_COMMAND_LINE_A;
- if (opt->do_line) command_line |= PM_OPTIONS_COMMAND_LINE_L;
- if (opt->do_loop) command_line |= PM_OPTIONS_COMMAND_LINE_N;
- if (opt->do_print) command_line |= PM_OPTIONS_COMMAND_LINE_P;
- if (opt->xflag) command_line |= PM_OPTIONS_COMMAND_LINE_X;
-
+ uint8_t command_line = prism_script_command_line(opt);
VALUE error;
- if (strcmp(opt->script, "-") == 0) {
+
+ if (read_stdin) {
pm_options_command_line_set(options, command_line);
pm_options_filepath_set(options, "-");
+ pm_options_shebang_callback_set(options, prism_script_shebang_callback, (void *) opt);
- prism_opt_init(opt);
+ ruby_opt_init(opt);
error = pm_parse_stdin(result);
+
+ // 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) && pm_parser_data_loc(result->parser)->length != 0) {
+ rb_define_global_const("DATA", rb_stdin);
+ }
}
else if (opt->e_script) {
- command_line |= PM_OPTIONS_COMMAND_LINE_E;
+ command_line = (uint8_t) ((command_line | PM_OPTIONS_COMMAND_LINE_E) & ~PM_OPTIONS_COMMAND_LINE_X);
pm_options_command_line_set(options, command_line);
- prism_opt_init(opt);
- error = pm_parse_string(result, opt->e_script, rb_str_new2("-e"));
+ ruby_opt_init(opt);
+ result->node.coverage_enabled = 0;
+ error = pm_parse_string(result, opt->e_script, rb_str_new2("-e"), NULL);
}
else {
+ VALUE script_name = rb_str_encode_ospath(opt->script_name);
+
pm_options_command_line_set(options, command_line);
- error = pm_load_file(result, opt->script_name, true);
+ pm_options_shebang_callback_set(options, prism_script_shebang_callback, (void *) opt);
+
+ error = pm_load_file(result, script_name, true);
// 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);
+ ruby_opt_init(opt);
+ error = pm_parse_file(result, opt->script_name, NULL);
+ }
+
+ // Check if (after requiring all of the files through -r flags) we have
+ // coverage enabled and need to enable coverage on the main script.
+ if (RTEST(rb_get_coverages())) {
+ result->node.coverage_enabled = 1;
}
// 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) && pm_parser_data_loc(result->parser)->length != 0) {
int xflag = opt->xflag;
- VALUE file = open_load_file(opt->script_name, &xflag);
+ VALUE file = open_load_file(script_name, &xflag);
- const pm_parser_t *parser = &result->parser;
- size_t offset = parser->data_loc.start - parser->start + 7;
+ const pm_parser_t *parser = result->parser;
+ const pm_location_t *data_loc = pm_parser_data_loc(parser);
+ const uint8_t *start = pm_parser_start(parser);
+ const uint8_t *end = pm_parser_end(parser);
+ uint32_t offset = data_loc->start + 7;
- if ((parser->start + offset < parser->end) && parser->start[offset] == '\r') offset++;
- if ((parser->start + offset < parser->end) && parser->start[offset] == '\n') offset++;
+ if ((start + offset < end) && start[offset] == '\r') offset++;
+ if ((start + offset < end) && 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, UINT2NUM(offset), INT2FIX(SEEK_SET));
rb_define_global_const("DATA", file);
}
}
@@ -2196,11 +2311,11 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
static VALUE
prism_dump_tree(pm_parse_result_t *result)
{
- pm_buffer_t output_buffer = { 0 };
+ pm_buffer_t *output_buffer = pm_buffer_new();
- pm_prettyprint(&output_buffer, &result->parser, result->node.ast_node);
- VALUE tree = rb_str_new(output_buffer.value, output_buffer.length);
- pm_buffer_free(&output_buffer);
+ pm_prettyprint(output_buffer, result->parser, result->node.ast_node);
+ VALUE tree = rb_str_new(pm_buffer_value(output_buffer), pm_buffer_length(output_buffer));
+ pm_buffer_free(output_buffer);
return tree;
}
@@ -2236,10 +2351,20 @@ process_options_global_setup(const ruby_cmdline_options_t *opt, const rb_iseq_t
rb_exec_event_hook_script_compiled(ec, iseq, script);
}
+static bool
+has_dir_sep(const char *path)
+{
+ if (strchr(path, '/')) return true;
+#ifdef _WIN32
+ if (strchr(path, '\\')) return true;
+#endif
+ return false;
+}
+
static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
- VALUE vast = Qnil;
+ VALUE ast_value = Qnil;
struct {
rb_ast_t *ast;
pm_parse_result_t prism;
@@ -2257,8 +2382,8 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
char fbuf[MAXPATHLEN];
int i = (int)proc_options(argc, argv, opt, 0);
unsigned int dump = opt->dump & dump_exit_bits;
- rb_vm_t *vm = GET_VM();
- const long loaded_before_enc = RARRAY_LEN(vm->loaded_features);
+ const rb_box_t *box = rb_root_box();
+ const long loaded_before_enc = RARRAY_LEN(box->loaded_features);
if (opt->dump & (DUMP_BIT(usage)|DUMP_BIT(help))) {
const char *const progname =
@@ -2288,20 +2413,33 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
FEATURE_SET(opt->features, FEATURE_BIT(yjit));
}
#endif
+#if USE_ZJIT
+ if (!FEATURE_USED_P(opt->features, zjit) && env_var_truthy("RUBY_ZJIT_ENABLE")) {
+ FEATURE_SET(opt->features, FEATURE_BIT(zjit));
+
+ // When the --zjit flag is specified, we would have call setup_zjit_options(""),
+ // which would have called rb_zjit_prepare_options() internally. This ensures we
+ // go through the same set up but with less overhead than setup_zjit_options("").
+ extern void rb_zjit_prepare_options();
+ rb_zjit_prepare_options();
+ }
+#endif
}
if (MULTI_BITS_P(FEATURE_SET_BITS(opt->features) & feature_jit_mask)) {
- rb_warn("RJIT and YJIT cannot both be enabled at the same time. Exiting");
+ rb_warn("Only one JIT can be enabled at the same time. Exiting");
return Qfalse;
}
-#if USE_RJIT
- if (FEATURE_SET_P(opt->features, rjit)) {
- opt->rjit.on = true; // set opt->rjit.on for Init_ruby_description() and calling rb_rjit_init()
- }
-#endif
#if USE_YJIT
if (FEATURE_SET_P(opt->features, yjit)) {
- opt->yjit = true; // set opt->yjit for Init_ruby_description() and calling rb_yjit_init()
+ bool rb_yjit_option_disable(void);
+ opt->yjit = !rb_yjit_option_disable(); // set opt->yjit for Init_ruby_description() and calling rb_yjit_init()
+ }
+#endif
+#if USE_ZJIT
+ if (FEATURE_SET_P(opt->features, zjit)) {
+ bool rb_zjit_option_enable(void);
+ opt->zjit = rb_zjit_option_enable(); // set opt->zjit for Init_ruby_description() and calling rb_zjit_init()
}
#endif
@@ -2328,7 +2466,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (!opt->script || opt->script[0] == '\0') {
opt->script = "-";
}
- else if (opt->do_search) {
+ else if (opt->do_search && !has_dir_sep(opt->script)) {
const char *path = getenv("RUBYPATH");
opt->script = 0;
@@ -2398,7 +2536,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_obj_freeze(opt->script_name);
if (IF_UTF8_PATH(uenc != lenc, 1)) {
long i;
- VALUE load_path = vm->load_path;
+ VALUE load_path = box->load_path;
const ID id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK;
int modifiable = FALSE;
@@ -2421,11 +2559,11 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
RARRAY_ASET(load_path, i, path);
}
if (modifiable) {
- rb_ary_replace(vm->load_path_snapshot, load_path);
+ rb_ary_replace(box->load_path_snapshot, load_path);
}
}
{
- VALUE loaded_features = vm->loaded_features;
+ VALUE loaded_features = box->loaded_features;
bool modified = false;
for (long i = loaded_before_enc; i < RARRAY_LEN(loaded_features); ++i) {
VALUE path = RARRAY_AREF(loaded_features, i);
@@ -2437,7 +2575,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
RARRAY_ASET(loaded_features, i, path);
}
if (modified) {
- rb_ary_replace(vm->loaded_features_snapshot, loaded_features);
+ rb_ary_replace(box->loaded_features_snapshot, loaded_features);
}
}
@@ -2446,7 +2584,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#define SET_COMPILE_OPTION(h, o, name) \
rb_hash_aset((h), ID2SYM(rb_intern_const(#name)), \
RBOOL(FEATURE_SET_P(o->features, name)))
- SET_COMPILE_OPTION(option, opt, frozen_string_literal);
+
+ if (FEATURE_SET_P(opt->features, frozen_string_literal_set)) {
+ SET_COMPILE_OPTION(option, opt, frozen_string_literal);
+ }
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
rb_funcallv(rb_cISeq, rb_intern_const("compile_option="), 1, &option);
#undef SET_COMPILE_OPTION
@@ -2473,9 +2614,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_enc_associate(opt->e_script, eenc);
}
- if (!(*rb_ruby_prism_ptr())) {
- vast = process_script(opt);
- if (!(result.ast = rb_ruby_ast_data_get(vast))) return Qfalse;
+ if (!rb_ruby_prism_p()) {
+ ast_value = process_script(opt);
+ if (!(result.ast = rb_ruby_ast_data_get(ast_value))) return Qfalse;
}
else {
prism_script(opt, &result.prism);
@@ -2509,7 +2650,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (dump & DUMP_BIT(syntax)) {
printf("Syntax OK\n");
dump &= ~DUMP_BIT(syntax);
- if (!dump) return Qtrue;
+ if (!dump) {
+ dispose_result();
+ return Qtrue;
+ }
}
if (dump & DUMP_BIT(parsetree)) {
@@ -2552,12 +2696,19 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (!result.ast) {
pm_parse_result_t *pm = &result.prism;
- iseq = pm_iseq_new_main(&pm->node, opt->script_name, path, parent, optimize);
+ int error_state;
+ iseq = pm_iseq_new_main(&pm->node, opt->script_name, path, parent, optimize, &error_state);
+
pm_parse_result_free(pm);
+
+ if (error_state) {
+ RUBY_ASSERT(iseq == NULL);
+ rb_jump_tag(error_state);
+ }
}
else {
rb_ast_t *ast = result.ast;
- iseq = rb_iseq_new_main(vast, opt->script_name, path, parent, optimize);
+ iseq = rb_iseq_new_main(ast_value, opt->script_name, path, parent, optimize);
rb_ast_dispose(ast);
}
}
@@ -2608,7 +2759,7 @@ load_file_internal(VALUE argp_v)
ruby_cmdline_options_t *opt = argp->opt;
VALUE f = argp->f;
int line_start = 1;
- VALUE vast = Qnil;
+ VALUE ast_value = Qnil;
rb_encoding *enc;
ID set_encoding;
@@ -2709,7 +2860,7 @@ load_file_internal(VALUE argp_v)
return rb_parser_compile_string_path(parser, orig_fname, f, line_start);
}
rb_funcall(f, set_encoding, 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
- vast = rb_parser_compile_file_path(parser, orig_fname, f, line_start);
+ ast_value = rb_parser_compile_file_path(parser, orig_fname, f, line_start);
rb_funcall(f, set_encoding, 1, rb_parser_encoding(parser));
if (script && rb_parser_end_seen_p(parser)) {
/*
@@ -2727,7 +2878,7 @@ load_file_internal(VALUE argp_v)
rb_define_global_const("DATA", f);
argp->f = Qnil;
}
- return vast;
+ return ast_value;
}
/* disabling O_NONBLOCK, and returns 0 on success, otherwise errno */
@@ -2859,9 +3010,9 @@ rb_load_file(const char *fname)
void *
rb_load_file_str(VALUE fname_v)
{
- VALUE vast;
- vast = rb_parser_load_file(rb_parser_new(), fname_v);
- return (void *)rb_ruby_ast_data_get(vast);
+ VALUE ast_value;
+ ast_value = rb_parser_load_file(rb_parser_new(), fname_v);
+ return (void *)rb_ruby_ast_data_get(ast_value);
}
VALUE
@@ -3087,8 +3238,6 @@ ruby_process_options(int argc, char **argv)
VALUE iseq;
const char *script_name = (argc > 0 && argv[0]) ? argv[0] : ruby_engine;
- (*rb_ruby_prism_ptr()) = false;
-
if (!origarg.argv || origarg.argc <= 0) {
origarg.argc = argc;
origarg.argv = argv;
@@ -3101,12 +3250,18 @@ ruby_process_options(int argc, char **argv)
ruby_init_setproctitle(argc, argv);
#endif
+ if (getenv("RUBY_FREE_AT_EXIT")) {
+ rb_free_at_exit = true;
+ rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL, "Free at exit is experimental and may be unstable");
+ }
+
iseq = process_options(argc, argv, cmdline_options_init(&opt));
if (opt.crash_report && *opt.crash_report) {
void ruby_set_crash_report(const char *template);
ruby_set_crash_report(opt.crash_report);
}
+
return (void*)(struct RData*)iseq;
}
@@ -3152,3 +3307,9 @@ ruby_sysinit(int *argc, char ***argv)
}
fill_standard_fds();
}
+
+#ifdef RUBY_ASAN_ENABLED
+RUBY_SYMBOL_EXPORT_BEGIN
+const char ruby_asan_default_options[] = "use_sigaltstack=0:detect_leaks=0";
+RUBY_SYMBOL_EXPORT_END
+#endif