summaryrefslogtreecommitdiff
path: root/ruby.c
diff options
context:
space:
mode:
Diffstat (limited to 'ruby.c')
-rw-r--r--ruby.c571
1 files changed, 345 insertions, 226 deletions
diff --git a/ruby.c b/ruby.c
index d1ae574cdf..28f43176d6 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),
@@ -151,22 +152,19 @@ enum feature_flag_bits {
SEP \
X(parsetree) \
SEP \
- X(parsetree_with_comment) \
- SEP \
X(insns) \
- SEP \
- X(insns_without_opt) \
/* END OF DUMPS */
enum dump_flag_bits {
dump_version_v,
- dump_error_tolerant,
+ dump_opt_error_tolerant,
+ dump_opt_comment,
+ dump_opt_optimize,
EACH_DUMPS(DEFINE_DUMP, COMMA),
- dump_error_tolerant_bits = (DUMP_BIT(yydebug) |
- DUMP_BIT(parsetree) |
- DUMP_BIT(parsetree_with_comment)),
dump_exit_bits = (DUMP_BIT(yydebug) | DUMP_BIT(syntax) |
- DUMP_BIT(parsetree) | DUMP_BIT(parsetree_with_comment) |
- DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))
+ DUMP_BIT(parsetree) | DUMP_BIT(insns)),
+ dump_optional_bits = (DUMP_BIT(opt_error_tolerant) |
+ DUMP_BIT(opt_comment) |
+ DUMP_BIT(opt_optimize))
};
static inline void
@@ -192,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 = (
@@ -200,6 +199,7 @@ enum {
& ~FEATURE_BIT(gems)
#endif
& ~FEATURE_BIT(frozen_string_literal)
+ & ~FEATURE_BIT(frozen_string_literal_set)
& ~feature_jit_mask
)
};
@@ -217,17 +217,16 @@ 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);
opt->backtrace_length_limit = LONG_MIN;
return opt;
}
-static rb_ast_t *load_file(VALUE parser, VALUE fname, VALUE f, int script,
+static VALUE load_file(VALUE parser, VALUE fname, VALUE f, int script,
ruby_cmdline_options_t *opt);
static VALUE open_load_file(VALUE fname_v, int *xflag);
static void forbid_setid(const char *, const ruby_cmdline_options_t *);
@@ -306,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.
@@ -325,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."),
@@ -345,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."),
@@ -372,11 +375,12 @@ usage(const char *name, int help, int highlight, int columns)
M("-y", ", --yydebug", "Print parser log; backward compatibility not guaranteed."),
};
static const struct ruby_opt_message dumps[] = {
- M("insns", "", "Instruction sequences."),
- M("insns_without_opt", "", "Instruction sequences compiled with no optimization."),
- M("yydebug(+error-tolerant)", "", "yydebug of yacc parser generator."),
- M("parsetree(+error-tolerant)", "", "Abstract syntax tree (AST)."),
- M("parsetree_with_comment(+error-tolerant)", "", "AST with comments."),
+ M("insns", "", "Instruction sequences."),
+ M("yydebug", "", "yydebug of yacc parser generator."),
+ 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 with --parser=parse.y)."),
};
static const struct ruby_opt_message features[] = {
M("gems", "", "Rubygems (only for debugging, default: "DEFAULT_RUBYGEMS_ENABLED")."),
@@ -388,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;
@@ -430,49 +432,41 @@ 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
}
#define rubylib_path_new rb_str_new
static void
-push_include(const char *path, VALUE (*filter)(VALUE))
+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;
-
- p = path;
- while (*p) {
- while (*p == sep)
- p++;
- if (!*p) break;
- for (s = p; *s && *s != sep; s = CharNext(s));
- rb_ary_push(load_path, (*filter)(rubylib_path_new(p, s - p)));
- p = s;
- }
-}
-
+ VALUE load_path = rb_root_box()->load_path;
#ifdef __CYGWIN__
-static void
-push_include_cygwin(const char *path, VALUE (*filter)(VALUE))
-{
- const char *p, *s;
char rubylib[FILENAME_MAX];
VALUE buf = 0;
+# define is_path_sep(c) ((c) == sep || (c) == ';')
+#else
+# define is_path_sep(c) ((c) == sep)
+#endif
+ if (path == 0) return;
p = path;
while (*p) {
- unsigned int len;
- while (*p == ';')
+ long len;
+ while (is_path_sep(*p))
p++;
if (!*p) break;
- for (s = p; *s && *s != ';'; s = CharNext(s));
+ for (s = p; *s && !is_path_sep(*s); s = CharNext(s));
len = s - p;
+#undef is_path_sep
+
+#ifdef __CYGWIN__
if (*s) {
if (!buf) {
buf = rb_str_new(p, len);
@@ -489,23 +483,14 @@ push_include_cygwin(const char *path, VALUE (*filter)(VALUE))
#else
# error no cygwin_conv_path
#endif
- if (CONV_TO_POSIX_PATH(p, rubylib) == 0)
+ if (CONV_TO_POSIX_PATH(p, rubylib) == 0) {
p = rubylib;
- push_include(p, filter);
- if (!*s) break;
- p = s + 1;
- }
-}
-
-#define push_include push_include_cygwin
+ len = strlen(p);
+ }
#endif
-
-void
-ruby_push_include(const char *path, VALUE (*filter)(VALUE))
-{
- if (path == 0)
- return;
- push_include(path, filter);
+ rb_ary_push(load_path, (*filter)(rubylib_path_new(p, len)));
+ p = s;
+ }
}
static VALUE
@@ -513,6 +498,7 @@ identical_path(VALUE path)
{
return path;
}
+
static VALUE
locale_path(VALUE path)
{
@@ -573,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
@@ -723,11 +711,11 @@ ruby_init_loadpath(void)
p -= bindir_len;
archlibdir = rb_str_subseq(sopath, 0, p - libpath);
rb_str_cat_cstr(archlibdir, libdir);
- OBJ_FREEZE_RAW(archlibdir);
+ OBJ_FREEZE(archlibdir);
}
else if (p - libpath >= libdir_len && !strncmp(p - libdir_len, libdir, libdir_len)) {
archlibdir = rb_str_subseq(sopath, 0, (p2 ? p2 : p) - libpath);
- OBJ_FREEZE_RAW(archlibdir);
+ OBJ_FREEZE(archlibdir);
p -= libdir_len;
}
#ifdef ENABLE_MULTIARCH
@@ -758,12 +746,12 @@ ruby_init_loadpath(void)
#endif
rb_gc_register_address(&ruby_prefix_path);
ruby_prefix_path = PREFIX_PATH();
- OBJ_FREEZE_RAW(ruby_prefix_path);
+ OBJ_FREEZE(ruby_prefix_path);
if (!archlibdir) archlibdir = ruby_prefix_path;
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);
@@ -775,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);
}
@@ -927,7 +913,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) {
@@ -959,7 +947,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);
@@ -978,7 +967,7 @@ name_match_p(const char *name, const char *str, size_t len)
if (*str != '-' && *str != '_') return 0;
while (ISALNUM(*name)) name++;
if (*name != '-' && *name != '_') return 0;
- ++name;
+ if (!*++name) return 1;
++str;
if (--len == 0) return 1;
}
@@ -1022,7 +1011,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;
}
@@ -1050,6 +1039,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;
}
@@ -1098,21 +1090,45 @@ memtermspn(const char *str, char term, int len)
static const char additional_opt_sep = '+';
static unsigned int
-dump_additional_option(const char *str, int len, unsigned int bits, const char *name)
+dump_additional_option_flag(const char *str, int len, unsigned int bits, bool set)
+{
+#define SET_DUMP_OPT(bit) if (NAME_MATCH_P(#bit, str, len)) { \
+ return set ? (bits | DUMP_BIT(opt_ ## bit)) : (bits & ~DUMP_BIT(opt_ ## bit)); \
+ }
+ SET_DUMP_OPT(error_tolerant);
+ SET_DUMP_OPT(comment);
+ SET_DUMP_OPT(optimize);
+#undef SET_DUMP_OPT
+ rb_warn("don't know how to dump with%s '%.*s'", set ? "" : "out", len, str);
+ return bits;
+}
+
+static unsigned int
+dump_additional_option(const char *str, int len, unsigned int bits)
{
int w;
for (; len-- > 0 && *str++ == additional_opt_sep; len -= w, str += w) {
w = memtermspn(str, additional_opt_sep, len);
-#define SET_ADDITIONAL(bit) if (NAME_MATCH_P(#bit, str, w)) { \
- if (bits & DUMP_BIT(bit)) \
- rb_warn("duplicate option to dump %s: '%.*s'", name, w, str); \
- bits |= DUMP_BIT(bit); \
- continue; \
+ bool set = true;
+ if (*str == '-' || *str == '+') {
+ set = *str++ == '+';
+ --w;
}
- if (dump_error_tolerant_bits & bits) {
- SET_ADDITIONAL(error_tolerant);
+ else {
+ int n = memtermspn(str, '-', w);
+ if (str[n] == '-') {
+ if (NAME_MATCH_P("with", str, n)) {
+ str += n;
+ w -= n;
+ }
+ else if (NAME_MATCH_P("without", str, n)) {
+ set = false;
+ str += n;
+ w -= n;
+ }
+ }
}
- rb_warn("don't know how to dump %s with '%.*s'", name, w, str);
+ bits = dump_additional_option_flag(str, w, bits, set);
}
return bits;
}
@@ -1121,12 +1137,17 @@ static void
dump_option(const char *str, int len, void *arg)
{
static const char list[] = EACH_DUMPS(LITERAL_NAME_ELEMENT, ", ");
+ unsigned int *bits_ptr = (unsigned int *)arg;
+ if (*str == '+' || *str == '-') {
+ bool set = *str++ == '+';
+ *bits_ptr = dump_additional_option_flag(str, --len, *bits_ptr, set);
+ return;
+ }
int w = memtermspn(str, additional_opt_sep, len);
#define SET_WHEN_DUMP(bit) \
- if (NAME_MATCH_P(#bit, (str), (w))) { \
- *(unsigned int *)arg |= \
- dump_additional_option(str + w, len - w, DUMP_BIT(bit), #bit); \
+ if (NAME_MATCH_P(#bit "-", (str), (w))) { \
+ *bits_ptr = dump_additional_option(str + w, len - w, *bits_ptr | DUMP_BIT(bit)); \
return; \
}
EACH_DUMPS(SET_WHEN_DUMP, ;);
@@ -1182,6 +1203,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:
*
@@ -1213,6 +1247,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);
}
@@ -1323,11 +1360,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;
}
@@ -1410,10 +1447,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);
@@ -1433,21 +1470,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));
@@ -1457,6 +1485,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);
@@ -1697,11 +1734,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 */
@@ -1724,7 +1777,6 @@ 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);
@@ -1759,28 +1811,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
}
}
- /* [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();
@@ -1791,15 +1821,20 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
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
+ /* Initialize the main box after loading libraries (including rubygems)
+ * to enable those in both root and main */
+ if (rb_box_available())
+ rb_initialize_main_box();
+ rb_box_init_done();
+
+ // Initialize JITs after ruby_init_prelude() because JITing prelude is typically not optimal.
#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);
@@ -1965,7 +2000,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)
@@ -2044,17 +2079,19 @@ show_help(const char *progname, int help)
usage(progname, help, tty, columns);
}
-static rb_ast_t *
+static VALUE
process_script(ruby_cmdline_options_t *opt)
{
rb_ast_t *ast;
+ VALUE ast_value;
VALUE parser = rb_parser_new();
+ const unsigned int dump = opt->dump;
- if (opt->dump & DUMP_BIT(yydebug)) {
+ if (dump & DUMP_BIT(yydebug)) {
rb_parser_set_yydebug(parser, Qtrue);
}
- if (opt->dump & DUMP_BIT(error_tolerant)) {
+ if ((dump & dump_exit_bits) && (dump & DUMP_BIT(opt_error_tolerant))) {
rb_parser_error_tolerant(parser);
}
@@ -2066,7 +2103,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);
- ast = 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;
@@ -2074,33 +2111,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);
- ast = 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(ast_value);
if (!ast->body.root) {
rb_ast_dispose(ast);
- return NULL;
+ return Qnil;
}
- return ast;
+ 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);
}
}
@@ -2115,39 +2175,63 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
pm_options_t *options = &result->options;
pm_options_line_set(options, 1);
+ pm_options_main_script_set(options, true);
- 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;
+ const bool read_stdin = (strcmp(opt->script, "-") == 0);
+ 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 = 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) && result->parser.data_loc.start != NULL) {
+ 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);
+ 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
@@ -2155,7 +2239,7 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
// 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);
+ 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;
@@ -2220,6 +2304,7 @@ process_options_global_setup(const ruby_cmdline_options_t *opt, const rb_iseq_t
static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
+ VALUE ast_value = Qnil;
struct {
rb_ast_t *ast;
pm_parse_result_t prism;
@@ -2237,8 +2322,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 =
@@ -2268,20 +2353,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
@@ -2334,8 +2432,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#ifdef _WIN32
translit_char_bin(RSTRING_PTR(opt->script_name), '\\', '/');
-#elif defined DOSISH
- translit_char(RSTRING_PTR(opt->script_name), '\\', '/');
#endif
ruby_gc_set_params();
@@ -2380,7 +2476,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;
@@ -2403,11 +2499,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);
@@ -2419,7 +2515,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);
}
}
@@ -2428,7 +2524,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
@@ -2455,8 +2554,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_enc_associate(opt->e_script, eenc);
}
- if (!(*rb_ruby_prism_ptr())) {
- if (!(result.ast = process_script(opt))) 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);
@@ -2490,13 +2590,16 @@ 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)|DUMP_BIT(parsetree_with_comment))) {
+ if (dump & DUMP_BIT(parsetree)) {
VALUE tree;
if (result.ast) {
- int comment = dump & DUMP_BIT(parsetree_with_comment);
+ int comment = opt->dump & DUMP_BIT(opt_comment);
tree = rb_parser_dump_tree(result.ast->body.root, comment);
}
else {
@@ -2504,7 +2607,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
}
rb_io_write(rb_stdout, tree);
rb_io_flush(rb_stdout);
- dump &= ~DUMP_BIT(parsetree)&~DUMP_BIT(parsetree_with_comment);
+ dump &= ~DUMP_BIT(parsetree);
if (!dump) {
dispose_result();
return Qtrue;
@@ -2529,21 +2632,28 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
GetBindingPtr(rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")), toplevel_binding);
const struct rb_block *base_block = toplevel_context(toplevel_binding);
const rb_iseq_t *parent = vm_block_iseq(base_block);
- bool optimize = !(dump & DUMP_BIT(insns_without_opt));
+ bool optimize = (opt->dump & DUMP_BIT(opt_optimize)) != 0;
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(&ast->body, opt->script_name, path, parent, optimize);
+ iseq = rb_iseq_new_main(ast_value, opt->script_name, path, parent, optimize);
rb_ast_dispose(ast);
}
}
- if (dump & (DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))) {
+ if (dump & DUMP_BIT(insns)) {
rb_io_write(rb_stdout, rb_iseq_disasm((const rb_iseq_t *)iseq));
rb_io_flush(rb_stdout);
dump &= ~DUMP_BIT(insns);
@@ -2577,7 +2687,7 @@ struct load_file_arg {
VALUE f;
};
-VALUE rb_script_lines_for(VALUE path, bool add);
+void rb_set_script_lines_for(VALUE vparser, VALUE path);
static VALUE
load_file_internal(VALUE argp_v)
@@ -2589,7 +2699,7 @@ load_file_internal(VALUE argp_v)
ruby_cmdline_options_t *opt = argp->opt;
VALUE f = argp->f;
int line_start = 1;
- rb_ast_t *ast = 0;
+ VALUE ast_value = Qnil;
rb_encoding *enc;
ID set_encoding;
@@ -2682,18 +2792,15 @@ load_file_internal(VALUE argp_v)
rb_parser_set_options(parser, opt->do_print, opt->do_loop,
opt->do_line, opt->do_split);
- VALUE lines = rb_script_lines_for(orig_fname, true);
- if (!NIL_P(lines)) {
- rb_parser_set_script_lines(parser, lines);
- }
+ rb_set_script_lines_for(parser, orig_fname);
if (NIL_P(f)) {
f = rb_str_new(0, 0);
rb_enc_associate(f, enc);
- return (VALUE)rb_parser_compile_string_path(parser, orig_fname, f, line_start);
+ 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("-"));
- ast = 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)) {
/*
@@ -2711,7 +2818,7 @@ load_file_internal(VALUE argp_v)
rb_define_global_const("DATA", f);
argp->f = Qnil;
}
- return (VALUE)ast;
+ return ast_value;
}
/* disabling O_NONBLOCK, and returns 0 on success, otherwise errno */
@@ -2820,7 +2927,7 @@ restore_load_file(VALUE arg)
return Qnil;
}
-static rb_ast_t *
+static VALUE
load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t *opt)
{
struct load_file_arg arg;
@@ -2829,7 +2936,7 @@ load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t
arg.script = script;
arg.opt = opt;
arg.f = f;
- return (rb_ast_t *)rb_ensure(load_file_internal, (VALUE)&arg,
+ return rb_ensure(load_file_internal, (VALUE)&arg,
restore_load_file, (VALUE)&arg);
}
@@ -2843,10 +2950,12 @@ rb_load_file(const char *fname)
void *
rb_load_file_str(VALUE fname_v)
{
- return rb_parser_load_file(rb_parser_new(), fname_v);
+ VALUE ast_value;
+ ast_value = rb_parser_load_file(rb_parser_new(), fname_v);
+ return (void *)rb_ruby_ast_data_get(ast_value);
}
-void *
+VALUE
rb_parser_load_file(VALUE parser, VALUE fname_v)
{
ruby_cmdline_options_t opt;
@@ -3069,8 +3178,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;
@@ -3083,12 +3190,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;
}
@@ -3134,3 +3247,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