Implement chilled strings

[Feature #20205]

As a path toward enabling frozen string literals by default in the future,
this commit introduce "chilled strings". From a user perspective chilled
strings pretend to be frozen, but on the first attempt to mutate them,
they lose their frozen status and emit a warning rather than to raise a
`FrozenError`.

Implementation wise, `rb_compile_option_struct.frozen_string_literal` is
no longer a boolean but a tri-state of `enabled/disabled/unset`.

When code is compiled with frozen string literals neither explictly enabled
or disabled, string literals are compiled with a new `putchilledstring`
instruction. This instruction is identical to `putstring` except it marks
the String with the `STR_CHILLED (FL_USER3)` and `FL_FREEZE` flags.

Chilled strings have the `FL_FREEZE` flag as to minimize the need to check
for chilled strings across the codebase, and to improve compatibility with
C extensions.

Notes:
  - `String#freeze`: clears the chilled flag.
  - `String#-@`: acts as if the string was mutable.
  - `String#+@`: acts as if the string was mutable.
  - `String#clone`: copies the chilled flag.

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
This commit is contained in:
Étienne Barrié 2023-12-01 11:33:00 +01:00 committed by Jean Boussier
parent 86b15316a7
commit 12be40ae6b
36 changed files with 714 additions and 282 deletions

View File

@ -7,6 +7,12 @@ Note that each entry is kept to a minimum, see links for details.
## Language changes ## Language changes
* String literals in files without a `frozen_string_literal` comment now behave
as if they were frozen. If they are mutated a deprecation warning is emited.
These warnings can be enabled with `-W:deprecated` or by setting `Warning[:deprecated] = true`.
To disable this change you can run Ruby with the `--disable-frozen-string-literal` command line
argument. [Feature #20205]
* `it` is added to reference a block parameter. [[Feature #18980]] * `it` is added to reference a block parameter. [[Feature #18980]]
* Keyword splatting `nil` when calling methods is now supported. * Keyword splatting `nil` when calling methods is now supported.

View File

@ -1792,3 +1792,14 @@ assert_equal 'ok', %q{
} }
end # if !ENV['GITHUB_WORKFLOW'] end # if !ENV['GITHUB_WORKFLOW']
# Chilled strings are not shareable
assert_equal 'false', %q{
Ractor.shareable?("chilled")
}
# Chilled strings can be made shareable
assert_equal 'true', %q{
shareable = Ractor.make_shareable("chilled")
shareable == "chilled" && Ractor.shareable?(shareable)
}

View File

@ -4679,6 +4679,37 @@ assert_equal '[0, {1=>1}]', %q{
test(KwInit, [Hash.ruby2_keywords_hash({1 => 1})]) test(KwInit, [Hash.ruby2_keywords_hash({1 => 1})])
} }
# Chilled string setivar trigger warning
assert_equal 'literal string will be frozen in the future', %q{
Warning[:deprecated] = true
$VERBOSE = true
$warning = "no-warning"
module ::Warning
def self.warn(message)
$warning = message.split("warning: ").last.strip
end
end
class String
def setivar!
@ivar = 42
end
end
def setivar!(str)
str.setivar!
end
10.times { setivar!("mutable".dup) }
10.times do
setivar!("frozen".freeze)
rescue FrozenError
end
setivar!("chilled") # Emit warning
$warning
}
# arity=-2 cfuncs # arity=-2 cfuncs
assert_equal '["", "1/2", [0, [:ok, 1]]]', %q{ assert_equal '["", "1/2", [0, [:ok, 1]]]', %q{
def test_cases(file, chain) def test_cases(file, chain)

View File

@ -2244,7 +2244,10 @@ singleton_class_of(VALUE obj)
return klass; return klass;
case T_STRING: case T_STRING:
if (FL_TEST_RAW(obj, RSTRING_FSTR)) { if (CHILLED_STRING_P(obj)) {
CHILLED_STRING_MUTATED(obj);
}
else if (FL_TEST_RAW(obj, RSTRING_FSTR)) {
rb_raise(rb_eTypeError, "can't define singleton"); rb_raise(rb_eTypeError, "can't define singleton");
} }
} }

View File

@ -4723,7 +4723,7 @@ frozen_string_literal_p(const rb_iseq_t *iseq)
return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal > 0; return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal > 0;
} }
static inline int static inline bool
static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key) static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key)
{ {
switch (nd_type(node)) { switch (nd_type(node)) {
@ -10365,12 +10365,18 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
debugp_param("nd_lit", get_string_value(node)); debugp_param("nd_lit", get_string_value(node));
if (!popped) { if (!popped) {
VALUE lit = get_string_value(node); VALUE lit = get_string_value(node);
if (!frozen_string_literal_p(iseq)) { switch (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
case ISEQ_FROZEN_STRING_LITERAL_UNSET:
lit = rb_fstring(lit);
ADD_INSN1(ret, node, putchilledstring, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
break;
case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
lit = rb_fstring(lit); lit = rb_fstring(lit);
ADD_INSN1(ret, node, putstring, lit); ADD_INSN1(ret, node, putstring, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit); RB_OBJ_WRITTEN(iseq, Qundef, lit);
} break;
else { case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) { if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line)); VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
lit = rb_str_dup(lit); lit = rb_str_dup(lit);
@ -10382,6 +10388,9 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
} }
ADD_INSN1(ret, node, putobject, lit); ADD_INSN1(ret, node, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit); RB_OBJ_WRITTEN(iseq, Qundef, lit);
break;
default:
rb_bug("invalid frozen_string_literal");
} }
} }
break; break;

View File

@ -3860,6 +3860,12 @@ void
rb_error_frozen_object(VALUE frozen_obj) rb_error_frozen_object(VALUE frozen_obj)
{ {
rb_yjit_lazy_push_frame(GET_EC()->cfp->pc); rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
if (CHILLED_STRING_P(frozen_obj)) {
CHILLED_STRING_MUTATED(frozen_obj);
return;
}
VALUE debug_info; VALUE debug_info;
const ID created_info = id_debug_created_info; const ID created_info = id_debug_created_info;
VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ", VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ",

View File

@ -476,6 +476,8 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append(dc, ", \"embedded\":true"); dump_append(dc, ", \"embedded\":true");
if (FL_TEST(obj, RSTRING_FSTR)) if (FL_TEST(obj, RSTRING_FSTR))
dump_append(dc, ", \"fstring\":true"); dump_append(dc, ", \"fstring\":true");
if (CHILLED_STRING_P(obj))
dump_append(dc, ", \"chilled\":true");
if (STR_SHARED_P(obj)) if (STR_SHARED_P(obj))
dump_append(dc, ", \"shared\":true"); dump_append(dc, ", \"shared\":true");
else else

View File

@ -5,8 +5,12 @@
# - repository-url: URL from where clone for test # - repository-url: URL from where clone for test
# - revision: revision in repository-url to test # - revision: revision in repository-url to test
# if `revision` is not given, "v"+`version` or `version` will be used. # if `revision` is not given, "v"+`version` or `version` will be used.
minitest 5.22.3 https://github.com/minitest/minitest
power_assert 2.0.3 https://github.com/ruby/power_assert # Waiting for https://github.com/minitest/minitest/pull/991
minitest 5.22.3 https://github.com/Shopify/minitest b5f5202575894796e00109a8f8a5041b778991ee
# Waiting for https://github.com/ruby/power_assert/pull/48
power_assert 2.0.3 https://github.com/ruby/power_assert 78dd2ab3ccd93796d83c0b35b978c39bfabb938c
rake 13.1.0 https://github.com/ruby/rake rake 13.1.0 https://github.com/ruby/rake
test-unit 3.6.2 https://github.com/test-unit/test-unit test-unit 3.6.2 https://github.com/test-unit/test-unit
rexml 3.2.6 https://github.com/ruby/rexml rexml 3.2.6 https://github.com/ruby/rexml
@ -17,8 +21,8 @@ net-pop 0.1.2 https://github.com/ruby/net-pop
net-smtp 0.4.0.1 https://github.com/ruby/net-smtp net-smtp 0.4.0.1 https://github.com/ruby/net-smtp
matrix 0.4.2 https://github.com/ruby/matrix matrix 0.4.2 https://github.com/ruby/matrix
prime 0.1.2 https://github.com/ruby/prime prime 0.1.2 https://github.com/ruby/prime
rbs 3.4.4 https://github.com/ruby/rbs 61b412bc7ba00519e7d6d08450bd384990d94ea2 rbs 3.4.4 https://github.com/ruby/rbs ba7872795d5de04adb8ff500c0e6afdc81a041dd
typeprof 0.21.11 https://github.com/ruby/typeprof typeprof 0.21.11 https://github.com/ruby/typeprof b19a6416da3a05d57fadd6ffdadb382b6d236ca5
debug 1.9.1 https://github.com/ruby/debug 2d602636d99114d55a32fedd652c9c704446a749 debug 1.9.1 https://github.com/ruby/debug 2d602636d99114d55a32fedd652c9c704446a749
racc 1.7.3 https://github.com/ruby/racc racc 1.7.3 https://github.com/ruby/racc
mutex_m 0.2.0 https://github.com/ruby/mutex_m mutex_m 0.2.0 https://github.com/ruby/mutex_m

View File

@ -916,6 +916,9 @@ static inline void
RB_OBJ_FREEZE_RAW(VALUE obj) RB_OBJ_FREEZE_RAW(VALUE obj)
{ {
RB_FL_SET_RAW(obj, RUBY_FL_FREEZE); RB_FL_SET_RAW(obj, RUBY_FL_FREEZE);
if (TYPE(obj) == T_STRING) {
RB_FL_UNSET_RAW(obj, FL_USER3); // STR_CHILLED
}
} }
RUBY_SYMBOL_EXPORT_BEGIN RUBY_SYMBOL_EXPORT_BEGIN

View File

@ -190,7 +190,6 @@ RBIMPL_ATTR_NONNULL(())
*/ */
void rb_error_frozen(const char *what); void rb_error_frozen(const char *what);
RBIMPL_ATTR_NORETURN()
/** /**
* Identical to rb_error_frozen(), except it takes arbitrary Ruby object * Identical to rb_error_frozen(), except it takes arbitrary Ruby object
* instead of C's string. * instead of C's string.

View File

@ -375,7 +375,17 @@ putstring
() ()
(VALUE val) (VALUE val)
{ {
val = rb_ec_str_resurrect(ec, str); val = rb_ec_str_resurrect(ec, str, false);
}
/* put chilled string val. string will be copied but frozen in the future. */
DEFINE_INSN
putchilledstring
(VALUE str)
()
(VALUE val)
{
val = rb_ec_str_resurrect(ec, str, true);
} }
/* put concatenate strings */ /* put concatenate strings */

View File

@ -17,6 +17,7 @@
#define STR_NOEMBED FL_USER1 #define STR_NOEMBED FL_USER1
#define STR_SHARED FL_USER2 /* = ELTS_SHARED */ #define STR_SHARED FL_USER2 /* = ELTS_SHARED */
#define STR_CHILLED FL_USER3
#ifdef rb_fstring_cstr #ifdef rb_fstring_cstr
# undef rb_fstring_cstr # undef rb_fstring_cstr
@ -77,7 +78,7 @@ VALUE rb_id_quote_unprintable(ID);
VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, int kw_splat, VALUE passed_proc); VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, int kw_splat, VALUE passed_proc);
struct rb_execution_context_struct; struct rb_execution_context_struct;
VALUE rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str); VALUE rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str, bool chilled);
#define rb_fstring_lit(str) rb_fstring_new((str), rb_strlen_lit(str)) #define rb_fstring_lit(str) rb_fstring_new((str), rb_strlen_lit(str))
#define rb_fstring_literal(str) rb_fstring_lit(str) #define rb_fstring_literal(str) rb_fstring_lit(str)
@ -108,6 +109,26 @@ STR_SHARED_P(VALUE str)
return FL_ALL_RAW(str, STR_NOEMBED | STR_SHARED); return FL_ALL_RAW(str, STR_NOEMBED | STR_SHARED);
} }
static inline bool
CHILLED_STRING_P(VALUE obj)
{
return RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, STR_CHILLED);
}
static inline void
CHILLED_STRING_MUTATED(VALUE str)
{
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "literal string will be frozen in the future");
FL_UNSET_RAW(str, STR_CHILLED | FL_FREEZE);
}
static inline void
STR_CHILL_RAW(VALUE str)
{
// Chilled strings are always also frozen
FL_SET_RAW(str, STR_CHILLED | RUBY_FL_FREEZE);
}
static inline bool static inline bool
is_ascii_string(VALUE str) is_ascii_string(VALUE str)
{ {

31
iseq.c
View File

@ -720,18 +720,20 @@ finish_iseq_build(rb_iseq_t *iseq)
} }
static rb_compile_option_t COMPILE_OPTION_DEFAULT = { static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
OPT_INLINE_CONST_CACHE, /* int inline_const_cache; */ .inline_const_cache = OPT_INLINE_CONST_CACHE,
OPT_PEEPHOLE_OPTIMIZATION, /* int peephole_optimization; */ .peephole_optimization = OPT_PEEPHOLE_OPTIMIZATION,
OPT_TAILCALL_OPTIMIZATION, /* int tailcall_optimization */ .tailcall_optimization = OPT_TAILCALL_OPTIMIZATION,
OPT_SPECIALISED_INSTRUCTION, /* int specialized_instruction; */ .specialized_instruction = OPT_SPECIALISED_INSTRUCTION,
OPT_OPERANDS_UNIFICATION, /* int operands_unification; */ .operands_unification = OPT_OPERANDS_UNIFICATION,
OPT_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */ .instructions_unification = OPT_INSTRUCTIONS_UNIFICATION,
OPT_FROZEN_STRING_LITERAL, .frozen_string_literal = OPT_FROZEN_STRING_LITERAL,
OPT_DEBUG_FROZEN_STRING_LITERAL, .debug_frozen_string_literal = OPT_DEBUG_FROZEN_STRING_LITERAL,
TRUE, /* coverage_enabled */ .coverage_enabled = TRUE,
}; };
static const rb_compile_option_t COMPILE_OPTION_FALSE = {0}; static const rb_compile_option_t COMPILE_OPTION_FALSE = {
.frozen_string_literal = -1, // unspecified
};
int int
rb_iseq_opt_frozen_string_literal(void) rb_iseq_opt_frozen_string_literal(void)
@ -770,9 +772,11 @@ set_compile_option_from_ast(rb_compile_option_t *option, const rb_ast_body_t *as
{ {
#define SET_COMPILE_OPTION(o, a, mem) \ #define SET_COMPILE_OPTION(o, a, mem) \
((a)->mem < 0 ? 0 : ((o)->mem = (a)->mem > 0)) ((a)->mem < 0 ? 0 : ((o)->mem = (a)->mem > 0))
SET_COMPILE_OPTION(option, ast, frozen_string_literal);
SET_COMPILE_OPTION(option, ast, coverage_enabled); SET_COMPILE_OPTION(option, ast, coverage_enabled);
#undef SET_COMPILE_OPTION #undef SET_COMPILE_OPTION
if (ast->frozen_string_literal >= 0) {
option->frozen_string_literal = ast->frozen_string_literal;
}
return option; return option;
} }
@ -814,13 +818,14 @@ make_compile_option_value(rb_compile_option_t *option)
SET_COMPILE_OPTION(option, opt, specialized_instruction); SET_COMPILE_OPTION(option, opt, specialized_instruction);
SET_COMPILE_OPTION(option, opt, operands_unification); SET_COMPILE_OPTION(option, opt, operands_unification);
SET_COMPILE_OPTION(option, opt, instructions_unification); SET_COMPILE_OPTION(option, opt, instructions_unification);
SET_COMPILE_OPTION(option, opt, frozen_string_literal);
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal); SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
SET_COMPILE_OPTION(option, opt, coverage_enabled); SET_COMPILE_OPTION(option, opt, coverage_enabled);
SET_COMPILE_OPTION_NUM(option, opt, debug_level); SET_COMPILE_OPTION_NUM(option, opt, debug_level);
} }
#undef SET_COMPILE_OPTION #undef SET_COMPILE_OPTION
#undef SET_COMPILE_OPTION_NUM #undef SET_COMPILE_OPTION_NUM
VALUE frozen_string_literal = option->frozen_string_literal == -1 ? Qnil : RBOOL(option->frozen_string_literal);
rb_hash_aset(opt, ID2SYM(rb_intern("frozen_string_literal")), frozen_string_literal);
return opt; return opt;
} }
@ -1248,7 +1253,7 @@ pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V
pm_parse_result_t result = { 0 }; pm_parse_result_t result = { 0 };
pm_options_line_set(&result.options, NUM2INT(line)); pm_options_line_set(&result.options, NUM2INT(line));
pm_options_frozen_string_literal_set(&result.options, option.frozen_string_literal); pm_options_frozen_string_literal_init(&result, option.frozen_string_literal);
VALUE error; VALUE error;
if (RB_TYPE_P(src, T_FILE)) { if (RB_TYPE_P(src, T_FILE)) {

6
iseq.h
View File

@ -47,6 +47,10 @@ extern const ID rb_iseq_shared_exc_local_tbl[];
#define ISEQ_FLIP_CNT(iseq) ISEQ_BODY(iseq)->variable.flip_count #define ISEQ_FLIP_CNT(iseq) ISEQ_BODY(iseq)->variable.flip_count
#define ISEQ_FROZEN_STRING_LITERAL_ENABLED 1
#define ISEQ_FROZEN_STRING_LITERAL_DISABLED 0
#define ISEQ_FROZEN_STRING_LITERAL_UNSET -1
static inline rb_snum_t static inline rb_snum_t
ISEQ_FLIP_CNT_INCREMENT(const rb_iseq_t *iseq) ISEQ_FLIP_CNT_INCREMENT(const rb_iseq_t *iseq)
{ {
@ -227,7 +231,7 @@ struct rb_compile_option_struct {
unsigned int specialized_instruction: 1; unsigned int specialized_instruction: 1;
unsigned int operands_unification: 1; unsigned int operands_unification: 1;
unsigned int instructions_unification: 1; unsigned int instructions_unification: 1;
unsigned int frozen_string_literal: 1; signed int frozen_string_literal: 2; /* -1: not specified, 0: false, 1: true */
unsigned int debug_frozen_string_literal: 1; unsigned int debug_frozen_string_literal: 1;
unsigned int coverage_enabled: 1; unsigned int coverage_enabled: 1;
int debug_level; int debug_level;

View File

@ -57,6 +57,7 @@ module RubyVM::RJIT
when :putobject then putobject(jit, ctx, asm) when :putobject then putobject(jit, ctx, asm)
when :putspecialobject then putspecialobject(jit, ctx, asm) when :putspecialobject then putspecialobject(jit, ctx, asm)
when :putstring then putstring(jit, ctx, asm) when :putstring then putstring(jit, ctx, asm)
when :putchilledstring then putchilledstring(jit, ctx, asm)
when :concatstrings then concatstrings(jit, ctx, asm) when :concatstrings then concatstrings(jit, ctx, asm)
when :anytostring then anytostring(jit, ctx, asm) when :anytostring then anytostring(jit, ctx, asm)
when :toregexp then toregexp(jit, ctx, asm) when :toregexp then toregexp(jit, ctx, asm)
@ -776,6 +777,27 @@ module RubyVM::RJIT
asm.mov(C_ARGS[0], EC) asm.mov(C_ARGS[0], EC)
asm.mov(C_ARGS[1], to_value(put_val)) asm.mov(C_ARGS[1], to_value(put_val))
asm.mov(C_ARGS[2], 0)
asm.call(C.rb_ec_str_resurrect)
stack_top = ctx.stack_push(Type::TString)
asm.mov(stack_top, C_RET)
KeepCompiling
end
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
def putchilledstring(jit, ctx, asm)
put_val = jit.operand(0, ruby: true)
# Save the PC and SP because the callee will allocate
jit_prepare_routine_call(jit, ctx, asm)
asm.mov(C_ARGS[0], EC)
asm.mov(C_ARGS[1], to_value(put_val))
asm.mov(C_ARGS[2], 1)
asm.call(C.rb_ec_str_resurrect) asm.call(C.rb_ec_str_resurrect)
stack_top = ctx.stack_push(Type::TString) stack_top = ctx.stack_push(Type::TString)

View File

@ -28,16 +28,16 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta
} }
vm->builtin_function_table = table; vm->builtin_function_table = table;
static const rb_compile_option_t optimization = { static const rb_compile_option_t optimization = {
TRUE, /* unsigned int inline_const_cache; */ .inline_const_cache = TRUE,
TRUE, /* unsigned int peephole_optimization; */ .peephole_optimization = TRUE,
FALSE,/* unsigned int tailcall_optimization; */ .tailcall_optimization = FALSE,
TRUE, /* unsigned int specialized_instruction; */ .specialized_instruction = TRUE,
TRUE, /* unsigned int operands_unification; */ .operands_unification = TRUE,
TRUE, /* unsigned int instructions_unification; */ .instructions_unification = TRUE,
TRUE, /* unsigned int frozen_string_literal; */ .frozen_string_literal = TRUE,
FALSE, /* unsigned int debug_frozen_string_literal; */ .debug_frozen_string_literal = FALSE,
FALSE, /* unsigned int coverage_enabled; */ .coverage_enabled = FALSE,
0, /* int debug_level; */ .debug_level = 0,
}; };
const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization); const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization);
GET_VM()->builtin_function_table = NULL; GET_VM()->builtin_function_table = NULL;

View File

@ -502,7 +502,10 @@ rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
case Qnil: case Qnil:
rb_funcall(clone, id_init_clone, 1, obj); rb_funcall(clone, id_init_clone, 1, obj);
RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE; RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
if (RB_OBJ_FROZEN(obj)) { if (CHILLED_STRING_P(obj)) {
STR_CHILL_RAW(clone);
}
else if (RB_OBJ_FROZEN(obj)) {
rb_shape_t * next_shape = rb_shape_transition_shape_frozen(clone); rb_shape_t * next_shape = rb_shape_transition_shape_frozen(clone);
if (!rb_shape_obj_too_complex(clone) && next_shape->type == SHAPE_OBJ_TOO_COMPLEX) { if (!rb_shape_obj_too_complex(clone) && next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
rb_evict_ivars_to_hash(clone); rb_evict_ivars_to_hash(clone);

View File

@ -787,6 +787,44 @@ pm_interpolated_node_compile(pm_node_list_t *parts, rb_iseq_t *iseq, NODE dummy_
if (parts_size > 0) { if (parts_size > 0) {
VALUE current_string = Qnil; VALUE current_string = Qnil;
bool literal = true;
for (size_t index = 0; index < parts_size; index++) {
const pm_node_t *part = parts->nodes[index];
if (!PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
literal = false;
break;
}
}
if (literal) {
for (size_t index = 0; index < parts_size; index++) {
const pm_node_t *part = parts->nodes[index];
const pm_string_node_t *string_node = (const pm_string_node_t *)part;
VALUE string_value = parse_string_encoded(scope_node, (pm_node_t *)string_node, &string_node->unescaped);
if (RTEST(current_string)) {
current_string = rb_str_concat(current_string, string_value);
}
else {
current_string = string_value;
}
}
const pm_node_t *part = parts->nodes[0];
current_string = rb_fstring(current_string);
if (PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
ADD_INSN1(ret, &dummy_line_node, putobject, current_string);
}
else if (PM_NODE_FLAG_P(part, PM_STRING_FLAGS_MUTABLE)) {
ADD_INSN1(ret, &dummy_line_node, putstring, current_string);
}
else {
ADD_INSN1(ret, &dummy_line_node, putchilledstring, current_string);
}
return 1;
}
for (size_t index = 0; index < parts_size; index++) { for (size_t index = 0; index < parts_size; index++) {
const pm_node_t *part = parts->nodes[index]; const pm_node_t *part = parts->nodes[index];
@ -820,12 +858,7 @@ pm_interpolated_node_compile(pm_node_list_t *parts, rb_iseq_t *iseq, NODE dummy_
current_string = rb_enc_str_new(NULL, 0, scope_node->encoding); current_string = rb_enc_str_new(NULL, 0, scope_node->encoding);
} }
if (frozen_string_literal_p(iseq)) { ADD_INSN1(ret, &dummy_line_node, putobject, rb_fstring(current_string));
ADD_INSN1(ret, &dummy_line_node, putobject, rb_str_freeze(current_string));
}
else {
ADD_INSN1(ret, &dummy_line_node, putstring, rb_str_freeze(current_string));
}
current_string = Qnil; current_string = Qnil;
number_of_items_pushed++; number_of_items_pushed++;
@ -841,14 +874,7 @@ pm_interpolated_node_compile(pm_node_list_t *parts, rb_iseq_t *iseq, NODE dummy_
if (RTEST(current_string)) { if (RTEST(current_string)) {
current_string = rb_fstring(current_string); current_string = rb_fstring(current_string);
ADD_INSN1(ret, &dummy_line_node, putobject, current_string);
if (frozen_string_literal_p(iseq)) {
ADD_INSN1(ret, &dummy_line_node, putobject, current_string);
}
else {
ADD_INSN1(ret, &dummy_line_node, putstring, current_string);
}
current_string = Qnil; current_string = Qnil;
number_of_items_pushed++; number_of_items_pushed++;
} }
@ -7925,9 +7951,12 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) { if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
PUSH_INSN1(ret, location, putobject, string); PUSH_INSN1(ret, location, putobject, string);
} }
else { else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
PUSH_INSN1(ret, location, putstring, string); PUSH_INSN1(ret, location, putstring, string);
} }
else {
PUSH_INSN1(ret, location, putchilledstring, string);
}
} }
return; return;
} }
@ -7979,9 +8008,12 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) { if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
PUSH_INSN1(ret, location, putobject, value); PUSH_INSN1(ret, location, putobject, value);
} }
else { else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
PUSH_INSN1(ret, location, putstring, value); PUSH_INSN1(ret, location, putstring, value);
} }
else {
PUSH_INSN1(ret, location, putchilledstring, value);
}
} }
return; return;
} }
@ -8354,6 +8386,24 @@ pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t
return lines; return lines;
} }
void
pm_options_frozen_string_literal_init(pm_parse_result_t *result, int frozen_string_literal)
{
switch (frozen_string_literal) {
case ISEQ_FROZEN_STRING_LITERAL_UNSET:
break;
case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
pm_options_frozen_string_literal_set(&result->options, false);
break;
case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
pm_options_frozen_string_literal_set(&result->options, true);
break;
default:
rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
break;
}
}
/** /**
* Attempt to load the file into memory. Return a Ruby error if the file cannot * Attempt to load the file into memory. Return a Ruby error if the file cannot
* be read. * be read.

View File

@ -47,6 +47,7 @@ typedef struct {
bool parsed; bool parsed;
} pm_parse_result_t; } pm_parse_result_t;
void pm_options_frozen_string_literal_init(pm_parse_result_t *result, int frozen_string_literal);
VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath); 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_load_parse_file(pm_parse_result_t *result, VALUE filepath);

View File

@ -2985,7 +2985,10 @@ rb_obj_traverse(VALUE obj,
static int static int
frozen_shareable_p(VALUE obj, bool *made_shareable) frozen_shareable_p(VALUE obj, bool *made_shareable)
{ {
if (!RB_TYPE_P(obj, T_DATA)) { if (CHILLED_STRING_P(obj)) {
return false;
}
else if (!RB_TYPE_P(obj, T_DATA)) {
return true; return true;
} }
else if (RTYPEDDATA_P(obj)) { else if (RTYPEDDATA_P(obj)) {
@ -3014,6 +3017,17 @@ make_shareable_check_shareable(VALUE obj)
if (rb_ractor_shareable_p(obj)) { if (rb_ractor_shareable_p(obj)) {
return traverse_skip; return traverse_skip;
} }
else if (CHILLED_STRING_P(obj)) {
rb_funcall(obj, idFreeze, 0);
if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
}
if (RB_OBJ_SHAREABLE_P(obj)) {
return traverse_skip;
}
}
else if (!frozen_shareable_p(obj, &made_shareable)) { else if (!frozen_shareable_p(obj, &made_shareable)) {
if (made_shareable) { if (made_shareable) {
return traverse_skip; return traverse_skip;

2
ruby.c
View File

@ -2116,7 +2116,7 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
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_frozen_string_literal_set(&result->options, rb_iseq_opt_frozen_string_literal()); pm_options_frozen_string_literal_init(result, rb_iseq_opt_frozen_string_literal());
if (opt->ext.enc.name != 0) { if (opt->ext.enc.name != 0) {
pm_options_encoding_set(options, StringValueCStr(opt->ext.enc.name)); pm_options_encoding_set(options, StringValueCStr(opt->ext.enc.name));

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
frozen = "test".frozen?
interned = "test".equal?("test")
puts "frozen:#{frozen} interned:#{interned}"

View File

@ -0,0 +1,4 @@
# frozen_string_literal: false
frozen = "test".frozen?
interned = "test".equal?("test")
puts "frozen:#{frozen} interned:#{interned}"

View File

@ -0,0 +1,3 @@
frozen = "test".frozen?
interned = "test".equal?("test")
puts "frozen:#{frozen} interned:#{interned}"

View File

@ -19,6 +19,41 @@ describe "The --enable-frozen-string-literal flag causes string literals to" do
end end
end end
describe "The --disable-frozen-string-literal flag causes string literals to" do
it "produce a different object each time" do
ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb"), options: "--disable-frozen-string-literal").chomp.should == "false"
end
end
describe "With neither --enable-frozen-string-literal nor --disable-frozen-string-literal flag set" do
it "produce a different object each time" do
ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb")).chomp.should == "false"
end
ruby_version_is "3.4" do
it "if file has no frozen_string_literal comment produce different frozen strings each time" do
ruby_exe(fixture(__FILE__, "string_literal_raw.rb")).chomp.should == "frozen:true interned:false"
end
end
ruby_version_is ""..."3.4" do
it "if file has no frozen_string_literal comment produce different mutable strings each time" do
ruby_exe(fixture(__FILE__, "string_literal_raw.rb")).chomp.should == "frozen:false interned:false"
end
end
it "if file has frozen_string_literal:true comment produce same frozen strings each time" do
ruby_exe(fixture(__FILE__, "string_literal_frozen_comment.rb")).chomp.should == "frozen:true interned:true"
end
it "if file has frozen_string_literal:false comment produce different mutable strings each time" do
ruby_exe(fixture(__FILE__, "string_literal_mutable_comment.rb")).chomp.should == "frozen:false interned:false"
end
end
describe "The --debug flag produces" do describe "The --debug flag produces" do
it "debugging info on attempted frozen string modification" do it "debugging info on attempted frozen string modification" do
error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--debug', args: "2>&1") error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--debug', args: "2>&1")

View File

@ -350,9 +350,11 @@ CODE
end end
it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do
frozen_string_default = "test".frozen?
code = <<CODE.b code = <<CODE.b
# encoding: UTF-8 # encoding: UTF-8
# frozen_string_literal: true # frozen_string_literal: #{!frozen_string_default}
class EvalSpecs class EvalSpecs
Vπstring = "frozen" Vπstring = "frozen"
end end
@ -362,7 +364,7 @@ CODE
EvalSpecs.constants(false).should include(:"Vπstring") EvalSpecs.constants(false).should include(:"Vπstring")
EvalSpecs::Vπstring.should == "frozen" EvalSpecs::Vπstring.should == "frozen"
EvalSpecs::Vπstring.encoding.should == Encoding::UTF_8 EvalSpecs::Vπstring.encoding.should == Encoding::UTF_8
EvalSpecs::Vπstring.frozen?.should be_true EvalSpecs::Vπstring.frozen?.should == !frozen_string_default
end end
it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do
@ -381,8 +383,9 @@ CODE
end end
it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do
frozen_string_default = "test".frozen?
code = <<CODE.b code = <<CODE.b
# frozen_string_literal: true # frozen_string_literal: #{!frozen_string_default}
# encoding: UTF-8 # encoding: UTF-8
class EvalSpecs class EvalSpecs
Vπfrozen_first = "frozen" Vπfrozen_first = "frozen"
@ -396,24 +399,24 @@ CODE
value = EvalSpecs.const_get(binary_constant) value = EvalSpecs.const_get(binary_constant)
value.should == "frozen" value.should == "frozen"
value.encoding.should == Encoding::BINARY value.encoding.should == Encoding::BINARY
value.frozen?.should be_true value.frozen?.should == !frozen_string_default
end end
it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do
default_frozen_string_literal = "test".frozen? frozen_string_default = "test".frozen?
code = <<CODE code = <<CODE
some_token_before_magic_comment = :anything some_token_before_magic_comment = :anything
# frozen_string_literal: true # frozen_string_literal: #{!frozen_string_default}
class EvalSpecs class EvalSpecs
Vπstring_not_frozen = "not frozen" Vπstring_not_frozen = "not frozen"
end end
CODE CODE
-> { eval(code) }.should complain(/warning: [`']frozen_string_literal' is ignored after any tokens/, verbose: true) -> { eval(code) }.should complain(/warning: [`']frozen_string_literal' is ignored after any tokens/, verbose: true)
EvalSpecs::Vπstring_not_frozen.frozen?.should == default_frozen_string_literal EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
EvalSpecs.send :remove_const, :Vπstring_not_frozen EvalSpecs.send :remove_const, :Vπstring_not_frozen
-> { eval(code) }.should_not complain(verbose: false) -> { eval(code) }.should_not complain(verbose: false)
EvalSpecs::Vπstring_not_frozen.frozen?.should == default_frozen_string_literal EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
EvalSpecs.send :remove_const, :Vπstring_not_frozen EvalSpecs.send :remove_const, :Vπstring_not_frozen
end end
end end

View File

@ -0,0 +1,69 @@
require_relative '../../spec_helper'
describe "chilled String" do
guard -> { ruby_version_is "3.4" and !"test".equal?("test") } do
describe "#frozen?" do
it "returns true" do
"chilled".frozen?.should == true
end
end
describe "#-@" do
it "returns a different instance" do
input = "chilled"
interned = (-input)
interned.frozen?.should == true
interned.object_id.should_not == input.object_id
end
end
describe "#+@" do
it "returns a different instance" do
input = "chilled"
duped = (+input)
duped.frozen?.should == false
duped.object_id.should_not == input.object_id
end
end
describe "#clone" do
it "preserves chilled status" do
input = "chilled".clone
-> {
input << "-mutated"
}.should complain(/literal string will be frozen in the future/)
input.should == "chilled-mutated"
end
end
describe "mutation" do
it "emits a warning" do
input = "chilled"
-> {
input << "-mutated"
}.should complain(/literal string will be frozen in the future/)
input.should == "chilled-mutated"
end
it "emits a warning on singleton_class creaation" do
-> {
"chilled".singleton_class
}.should complain(/literal string will be frozen in the future/)
end
it "emits a warning on instance variable assignment" do
-> {
"chilled".instance_variable_set(:@ivar, 42)
}.should complain(/literal string will be frozen in the future/)
end
it "raises FrozenError after the string was explictly frozen" do
input = "chilled"
input.freeze
-> {
input << "mutated"
}.should raise_error(FrozenError)
end
end
end
end

View File

@ -232,8 +232,8 @@ describe "Ruby String literals" do
end end
it "produce different objects for literals with the same content in different files if the other file doesn't have the comment" do it "produce different objects for literals with the same content in different files if the other file doesn't have the comment" do
frozen_literals_by_default = eval("'test'").frozen? frozen_string_literal = "test".frozen? && "test".equal?("test")
ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_no_comment.rb")).chomp.should == (!frozen_literals_by_default).to_s ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_no_comment.rb")).chomp.should == (!frozen_string_literal).to_s
end end
it "produce different objects for literals with the same content in different files if they have different encodings" do it "produce different objects for literals with the same content in different files if they have different encodings" do

View File

@ -52,7 +52,7 @@ describe :object_id, shared: true do
o1.send(@method).should_not == o2.send(@method) o1.send(@method).should_not == o2.send(@method)
end end
guard -> { "test".frozen? } do # --enable-frozen-string-literal in $RUBYOPT guard -> { "test".frozen? && "test".equal?("test") } do # --enable-frozen-string-literal in $RUBYOPT
it "returns the same value for two identical String literals" do it "returns the same value for two identical String literals" do
o1 = "hello" o1 = "hello"
o2 = "hello" o2 = "hello"
@ -60,7 +60,17 @@ describe :object_id, shared: true do
end end
end end
guard_not -> { "test".frozen? } do guard -> { "test".frozen? && !"test".equal?("test") } do # chilled string literals
it "returns a different frozen value for two String literals" do
o1 = "hello"
o2 = "hello"
o1.send(@method).should_not == o2.send(@method)
o1.frozen?.should == true
o2.frozen?.should == true
end
end
guard -> { !"test".frozen? } do
it "returns a different value for two String literals" do it "returns a different value for two String literals" do
o1 = "hello" o1 = "hello"
o2 = "hello" o2 = "hello"

View File

@ -380,8 +380,9 @@ fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t data, int exist
OBJ_FREEZE_RAW(str); OBJ_FREEZE_RAW(str);
} }
else { else {
if (!OBJ_FROZEN(str)) if (!OBJ_FROZEN(str) || CHILLED_STRING_P(str)) {
str = str_new_frozen(rb_cString, str); str = str_new_frozen(rb_cString, str);
}
if (STR_SHARED_P(str)) { /* str should not be shared */ if (STR_SHARED_P(str)) { /* str should not be shared */
/* shared substring */ /* shared substring */
str_make_independent(str); str_make_independent(str);
@ -422,7 +423,7 @@ rb_fstring(VALUE str)
} }
} }
if (!FL_TEST_RAW(str, FL_FREEZE | STR_NOFREE)) if (!FL_TEST_RAW(str, FL_FREEZE | STR_NOFREE | STR_CHILLED))
rb_str_resize(str, RSTRING_LEN(str)); rb_str_resize(str, RSTRING_LEN(str));
fstr = register_fstring(str, FALSE); fstr = register_fstring(str, FALSE);
@ -1822,10 +1823,14 @@ rb_str_resurrect(VALUE str)
} }
VALUE VALUE
rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str) rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str, bool chilled)
{ {
RUBY_DTRACE_CREATE_HOOK(STRING, RSTRING_LEN(str)); RUBY_DTRACE_CREATE_HOOK(STRING, RSTRING_LEN(str));
return ec_str_duplicate(ec, rb_cString, str); VALUE new_str = ec_str_duplicate(ec, rb_cString, str);
if (chilled) {
STR_CHILL_RAW(new_str);
}
return new_str;
} }
/* /*
@ -3019,12 +3024,15 @@ str_substr(VALUE str, long beg, long len, int empty)
VALUE VALUE
rb_str_freeze(VALUE str) rb_str_freeze(VALUE str)
{ {
if (CHILLED_STRING_P(str)) {
FL_UNSET_RAW(str, STR_CHILLED);
}
if (OBJ_FROZEN(str)) return str; if (OBJ_FROZEN(str)) return str;
rb_str_resize(str, RSTRING_LEN(str)); rb_str_resize(str, RSTRING_LEN(str));
return rb_obj_freeze(str); return rb_obj_freeze(str);
} }
/* /*
* call-seq: * call-seq:
* +string -> new_string or self * +string -> new_string or self

View File

@ -592,22 +592,16 @@ module Prism
assert_prism_eval('$pit = 1; "1 #$pit 1"') assert_prism_eval('$pit = 1; "1 #$pit 1"')
assert_prism_eval('"1 #{1 + 2} 1"') assert_prism_eval('"1 #{1 + 2} 1"')
assert_prism_eval('"Prism" "::" "TestCompilePrism"') assert_prism_eval('"Prism" "::" "TestCompilePrism"')
assert_prism_eval('("a""b").frozen?') assert_prism_eval(<<-'RUBY')
assert_prism_eval(<<-CODE)
# frozen_string_literal: true # frozen_string_literal: true
("a""b").frozen? !("a""b""#{1}").frozen?
CODE RUBY
assert_prism_eval(<<-CODE) assert_prism_eval(<<-'RUBY')
# frozen_string_literal: true # frozen_string_literal: true
("a""b""#{1}").frozen? !("a""#{1}""b").frozen?
CODE RUBY
assert_prism_eval(<<-CODE)
# frozen_string_literal: true
("a""#{1}""b").frozen?
CODE
# Test encoding of interpolated strings # Test encoding of interpolated strings
assert_prism_eval(<<~'RUBY') assert_prism_eval(<<~'RUBY')
@ -620,6 +614,15 @@ module Prism
RUBY RUBY
end end
def test_concatenated_StringNode
assert_prism_eval('("a""b").frozen?')
assert_prism_eval(<<-CODE)
# frozen_string_literal: true
("a""b").frozen?
CODE
end
def test_InterpolatedSymbolNode def test_InterpolatedSymbolNode
assert_prism_eval('$pit = 1; :"1 #$pit 1"') assert_prism_eval('$pit = 1; :"1 #$pit 1"')
assert_prism_eval(':"1 #{1 + 2} 1"') assert_prism_eval(':"1 #{1 + 2} 1"')
@ -673,7 +676,9 @@ module Prism
def test_StringNode def test_StringNode
assert_prism_eval('"pit"') assert_prism_eval('"pit"')
assert_prism_eval('"a".frozen?') assert_prism_eval('"a".frozen?')
end
def test_StringNode_frozen_string_literal_true
[ [
# Test that string literal is frozen # Test that string literal is frozen
<<~RUBY, <<~RUBY,
@ -690,6 +695,31 @@ module Prism
end end
end end
def test_StringNode_frozen_string_literal_false
[
# Test that string literal is frozen
<<~RUBY,
# frozen_string_literal: false
!"a".frozen?
RUBY
# Test that two string literals with the same contents are the same string
<<~RUBY,
# frozen_string_literal: false
!"hello".equal?("hello")
RUBY
].each do |src|
assert_prism_eval(src, raw: true)
end
end
def test_StringNode_frozen_string_literal_default
# Test that string literal is chilled
assert_prism_eval('"a".frozen?')
# Test that two identical chilled string literals aren't the same object
assert_prism_eval('!"hello".equal?("hello")')
end
def test_SymbolNode def test_SymbolNode
assert_prism_eval(":pit") assert_prism_eval(":pit")
@ -2620,27 +2650,28 @@ end
private private
def compare_eval(source, raw:) def compare_eval(source, raw:, location:)
source = raw ? source : "class Prism::TestCompilePrism\n#{source}\nend" source = raw ? source : "class Prism::TestCompilePrism\n#{source}\nend"
ruby_eval = RubyVM::InstructionSequence.compile(source).eval ruby_eval = RubyVM::InstructionSequence.compile(source).eval
prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval
if ruby_eval.is_a? Proc if ruby_eval.is_a? Proc
assert_equal ruby_eval.class, prism_eval.class assert_equal ruby_eval.class, prism_eval.class, "@#{location.path}:#{location.lineno}"
else else
assert_equal ruby_eval, prism_eval assert_equal ruby_eval, prism_eval, "@#{location.path}:#{location.lineno}"
end end
end end
def assert_prism_eval(source, raw: false) def assert_prism_eval(source, raw: false)
location = caller_locations(1, 1).first
$VERBOSE, verbose_bak = nil, $VERBOSE $VERBOSE, verbose_bak = nil, $VERBOSE
begin begin
compare_eval(source, raw:) compare_eval(source, raw:, location:)
# Test "popped" functionality # Test "popped" functionality
compare_eval("#{source}; 1", raw:) compare_eval("#{source}; 1", raw:, location:)
ensure ensure
$VERBOSE = verbose_bak $VERBOSE = verbose_bak
end end

View File

@ -3610,6 +3610,39 @@ CODE
assert_bytesplice_raise(ArgumentError, S("hello"), 0..-1, "bye", 0, 3) assert_bytesplice_raise(ArgumentError, S("hello"), 0..-1, "bye", 0, 3)
end end
def test_chilled_string
chilled_string = eval('"chilled"')
# Chilled strings pretend to be frozen
assert_predicate chilled_string, :frozen?
assert_not_predicate chilled_string.dup, :frozen?
assert_predicate chilled_string.clone, :frozen?
# @+ treat the original string as frozen
assert_not_predicate +chilled_string, :frozen?
assert_not_same chilled_string, +chilled_string
# @- the the original string as mutable
assert_predicate -chilled_string, :frozen?
assert_not_same chilled_string, -chilled_string
end
def test_chilled_string_setivar
String.class_eval <<~RUBY, __FILE__, __LINE__ + 1
def setivar!
@ivar = 42
@ivar
end
RUBY
chilled_string = eval('"chilled"')
begin
assert_equal 42, chilled_string.setivar!
ensure
String.undef_method(:setivar!)
end
end
private private
def assert_bytesplice_result(expected, s, *args) def assert_bytesplice_result(expected, s, *args)

View File

@ -1663,7 +1663,7 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
pm_parse_result_t result = { 0 }; pm_parse_result_t result = { 0 };
pm_options_line_set(&result.options, line); pm_options_line_set(&result.options, line);
pm_options_frozen_string_literal_set(&result.options, rb_iseq_opt_frozen_string_literal()); pm_options_frozen_string_literal_init(&result, rb_iseq_opt_frozen_string_literal());
// Cout scopes, one for each parent iseq, plus one for our local scope // Cout scopes, one for each parent iseq, plus one for our local scope
int scopes_count = 0; int scopes_count = 0;

View File

@ -19,7 +19,7 @@
#define OPT_PEEPHOLE_OPTIMIZATION 1 #define OPT_PEEPHOLE_OPTIMIZATION 1
#define OPT_SPECIALISED_INSTRUCTION 1 #define OPT_SPECIALISED_INSTRUCTION 1
#define OPT_INLINE_CONST_CACHE 1 #define OPT_INLINE_CONST_CACHE 1
#define OPT_FROZEN_STRING_LITERAL 0 #define OPT_FROZEN_STRING_LITERAL -1
#define OPT_DEBUG_FROZEN_STRING_LITERAL 0 #define OPT_DEBUG_FROZEN_STRING_LITERAL 0
/* Build Options. /* Build Options.

View File

@ -2327,7 +2327,28 @@ fn gen_putstring(
let str_opnd = asm.ccall( let str_opnd = asm.ccall(
rb_ec_str_resurrect as *const u8, rb_ec_str_resurrect as *const u8,
vec![EC, put_val.into()] vec![EC, put_val.into(), 0.into()]
);
let stack_top = asm.stack_push(Type::TString);
asm.mov(stack_top, str_opnd);
Some(KeepCompiling)
}
fn gen_putchilledstring(
jit: &mut JITState,
asm: &mut Assembler,
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let put_val = jit.get_arg(0);
// Save the PC and SP because the callee will allocate
jit_prepare_call_with_gc(jit, asm);
let str_opnd = asm.ccall(
rb_ec_str_resurrect as *const u8,
vec![EC, put_val.into(), 1.into()]
); );
let stack_top = asm.stack_push(Type::TString); let stack_top = asm.stack_push(Type::TString);
@ -9778,6 +9799,7 @@ fn get_gen_fn(opcode: VALUE) -> Option<InsnGenFn> {
YARVINSN_pushtoarray => Some(gen_pushtoarray), YARVINSN_pushtoarray => Some(gen_pushtoarray),
YARVINSN_newrange => Some(gen_newrange), YARVINSN_newrange => Some(gen_newrange),
YARVINSN_putstring => Some(gen_putstring), YARVINSN_putstring => Some(gen_putstring),
YARVINSN_putchilledstring => Some(gen_putchilledstring),
YARVINSN_expandarray => Some(gen_expandarray), YARVINSN_expandarray => Some(gen_expandarray),
YARVINSN_defined => Some(gen_defined), YARVINSN_defined => Some(gen_defined),
YARVINSN_definedivar => Some(gen_definedivar), YARVINSN_definedivar => Some(gen_definedivar),

View File

@ -726,195 +726,197 @@ pub const YARVINSN_putself: ruby_vminsn_type = 18;
pub const YARVINSN_putobject: ruby_vminsn_type = 19; pub const YARVINSN_putobject: ruby_vminsn_type = 19;
pub const YARVINSN_putspecialobject: ruby_vminsn_type = 20; pub const YARVINSN_putspecialobject: ruby_vminsn_type = 20;
pub const YARVINSN_putstring: ruby_vminsn_type = 21; pub const YARVINSN_putstring: ruby_vminsn_type = 21;
pub const YARVINSN_concatstrings: ruby_vminsn_type = 22; pub const YARVINSN_putchilledstring: ruby_vminsn_type = 22;
pub const YARVINSN_anytostring: ruby_vminsn_type = 23; pub const YARVINSN_concatstrings: ruby_vminsn_type = 23;
pub const YARVINSN_toregexp: ruby_vminsn_type = 24; pub const YARVINSN_anytostring: ruby_vminsn_type = 24;
pub const YARVINSN_intern: ruby_vminsn_type = 25; pub const YARVINSN_toregexp: ruby_vminsn_type = 25;
pub const YARVINSN_newarray: ruby_vminsn_type = 26; pub const YARVINSN_intern: ruby_vminsn_type = 26;
pub const YARVINSN_newarraykwsplat: ruby_vminsn_type = 27; pub const YARVINSN_newarray: ruby_vminsn_type = 27;
pub const YARVINSN_pushtoarraykwsplat: ruby_vminsn_type = 28; pub const YARVINSN_newarraykwsplat: ruby_vminsn_type = 28;
pub const YARVINSN_duparray: ruby_vminsn_type = 29; pub const YARVINSN_pushtoarraykwsplat: ruby_vminsn_type = 29;
pub const YARVINSN_duphash: ruby_vminsn_type = 30; pub const YARVINSN_duparray: ruby_vminsn_type = 30;
pub const YARVINSN_expandarray: ruby_vminsn_type = 31; pub const YARVINSN_duphash: ruby_vminsn_type = 31;
pub const YARVINSN_concatarray: ruby_vminsn_type = 32; pub const YARVINSN_expandarray: ruby_vminsn_type = 32;
pub const YARVINSN_concattoarray: ruby_vminsn_type = 33; pub const YARVINSN_concatarray: ruby_vminsn_type = 33;
pub const YARVINSN_pushtoarray: ruby_vminsn_type = 34; pub const YARVINSN_concattoarray: ruby_vminsn_type = 34;
pub const YARVINSN_splatarray: ruby_vminsn_type = 35; pub const YARVINSN_pushtoarray: ruby_vminsn_type = 35;
pub const YARVINSN_splatkw: ruby_vminsn_type = 36; pub const YARVINSN_splatarray: ruby_vminsn_type = 36;
pub const YARVINSN_newhash: ruby_vminsn_type = 37; pub const YARVINSN_splatkw: ruby_vminsn_type = 37;
pub const YARVINSN_newrange: ruby_vminsn_type = 38; pub const YARVINSN_newhash: ruby_vminsn_type = 38;
pub const YARVINSN_pop: ruby_vminsn_type = 39; pub const YARVINSN_newrange: ruby_vminsn_type = 39;
pub const YARVINSN_dup: ruby_vminsn_type = 40; pub const YARVINSN_pop: ruby_vminsn_type = 40;
pub const YARVINSN_dupn: ruby_vminsn_type = 41; pub const YARVINSN_dup: ruby_vminsn_type = 41;
pub const YARVINSN_swap: ruby_vminsn_type = 42; pub const YARVINSN_dupn: ruby_vminsn_type = 42;
pub const YARVINSN_opt_reverse: ruby_vminsn_type = 43; pub const YARVINSN_swap: ruby_vminsn_type = 43;
pub const YARVINSN_topn: ruby_vminsn_type = 44; pub const YARVINSN_opt_reverse: ruby_vminsn_type = 44;
pub const YARVINSN_setn: ruby_vminsn_type = 45; pub const YARVINSN_topn: ruby_vminsn_type = 45;
pub const YARVINSN_adjuststack: ruby_vminsn_type = 46; pub const YARVINSN_setn: ruby_vminsn_type = 46;
pub const YARVINSN_defined: ruby_vminsn_type = 47; pub const YARVINSN_adjuststack: ruby_vminsn_type = 47;
pub const YARVINSN_definedivar: ruby_vminsn_type = 48; pub const YARVINSN_defined: ruby_vminsn_type = 48;
pub const YARVINSN_checkmatch: ruby_vminsn_type = 49; pub const YARVINSN_definedivar: ruby_vminsn_type = 49;
pub const YARVINSN_checkkeyword: ruby_vminsn_type = 50; pub const YARVINSN_checkmatch: ruby_vminsn_type = 50;
pub const YARVINSN_checktype: ruby_vminsn_type = 51; pub const YARVINSN_checkkeyword: ruby_vminsn_type = 51;
pub const YARVINSN_defineclass: ruby_vminsn_type = 52; pub const YARVINSN_checktype: ruby_vminsn_type = 52;
pub const YARVINSN_definemethod: ruby_vminsn_type = 53; pub const YARVINSN_defineclass: ruby_vminsn_type = 53;
pub const YARVINSN_definesmethod: ruby_vminsn_type = 54; pub const YARVINSN_definemethod: ruby_vminsn_type = 54;
pub const YARVINSN_send: ruby_vminsn_type = 55; pub const YARVINSN_definesmethod: ruby_vminsn_type = 55;
pub const YARVINSN_opt_send_without_block: ruby_vminsn_type = 56; pub const YARVINSN_send: ruby_vminsn_type = 56;
pub const YARVINSN_objtostring: ruby_vminsn_type = 57; pub const YARVINSN_opt_send_without_block: ruby_vminsn_type = 57;
pub const YARVINSN_opt_str_freeze: ruby_vminsn_type = 58; pub const YARVINSN_objtostring: ruby_vminsn_type = 58;
pub const YARVINSN_opt_nil_p: ruby_vminsn_type = 59; pub const YARVINSN_opt_str_freeze: ruby_vminsn_type = 59;
pub const YARVINSN_opt_str_uminus: ruby_vminsn_type = 60; pub const YARVINSN_opt_nil_p: ruby_vminsn_type = 60;
pub const YARVINSN_opt_newarray_send: ruby_vminsn_type = 61; pub const YARVINSN_opt_str_uminus: ruby_vminsn_type = 61;
pub const YARVINSN_invokesuper: ruby_vminsn_type = 62; pub const YARVINSN_opt_newarray_send: ruby_vminsn_type = 62;
pub const YARVINSN_invokeblock: ruby_vminsn_type = 63; pub const YARVINSN_invokesuper: ruby_vminsn_type = 63;
pub const YARVINSN_leave: ruby_vminsn_type = 64; pub const YARVINSN_invokeblock: ruby_vminsn_type = 64;
pub const YARVINSN_throw: ruby_vminsn_type = 65; pub const YARVINSN_leave: ruby_vminsn_type = 65;
pub const YARVINSN_jump: ruby_vminsn_type = 66; pub const YARVINSN_throw: ruby_vminsn_type = 66;
pub const YARVINSN_branchif: ruby_vminsn_type = 67; pub const YARVINSN_jump: ruby_vminsn_type = 67;
pub const YARVINSN_branchunless: ruby_vminsn_type = 68; pub const YARVINSN_branchif: ruby_vminsn_type = 68;
pub const YARVINSN_branchnil: ruby_vminsn_type = 69; pub const YARVINSN_branchunless: ruby_vminsn_type = 69;
pub const YARVINSN_once: ruby_vminsn_type = 70; pub const YARVINSN_branchnil: ruby_vminsn_type = 70;
pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 71; pub const YARVINSN_once: ruby_vminsn_type = 71;
pub const YARVINSN_opt_plus: ruby_vminsn_type = 72; pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 72;
pub const YARVINSN_opt_minus: ruby_vminsn_type = 73; pub const YARVINSN_opt_plus: ruby_vminsn_type = 73;
pub const YARVINSN_opt_mult: ruby_vminsn_type = 74; pub const YARVINSN_opt_minus: ruby_vminsn_type = 74;
pub const YARVINSN_opt_div: ruby_vminsn_type = 75; pub const YARVINSN_opt_mult: ruby_vminsn_type = 75;
pub const YARVINSN_opt_mod: ruby_vminsn_type = 76; pub const YARVINSN_opt_div: ruby_vminsn_type = 76;
pub const YARVINSN_opt_eq: ruby_vminsn_type = 77; pub const YARVINSN_opt_mod: ruby_vminsn_type = 77;
pub const YARVINSN_opt_neq: ruby_vminsn_type = 78; pub const YARVINSN_opt_eq: ruby_vminsn_type = 78;
pub const YARVINSN_opt_lt: ruby_vminsn_type = 79; pub const YARVINSN_opt_neq: ruby_vminsn_type = 79;
pub const YARVINSN_opt_le: ruby_vminsn_type = 80; pub const YARVINSN_opt_lt: ruby_vminsn_type = 80;
pub const YARVINSN_opt_gt: ruby_vminsn_type = 81; pub const YARVINSN_opt_le: ruby_vminsn_type = 81;
pub const YARVINSN_opt_ge: ruby_vminsn_type = 82; pub const YARVINSN_opt_gt: ruby_vminsn_type = 82;
pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 83; pub const YARVINSN_opt_ge: ruby_vminsn_type = 83;
pub const YARVINSN_opt_and: ruby_vminsn_type = 84; pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 84;
pub const YARVINSN_opt_or: ruby_vminsn_type = 85; pub const YARVINSN_opt_and: ruby_vminsn_type = 85;
pub const YARVINSN_opt_aref: ruby_vminsn_type = 86; pub const YARVINSN_opt_or: ruby_vminsn_type = 86;
pub const YARVINSN_opt_aset: ruby_vminsn_type = 87; pub const YARVINSN_opt_aref: ruby_vminsn_type = 87;
pub const YARVINSN_opt_aset_with: ruby_vminsn_type = 88; pub const YARVINSN_opt_aset: ruby_vminsn_type = 88;
pub const YARVINSN_opt_aref_with: ruby_vminsn_type = 89; pub const YARVINSN_opt_aset_with: ruby_vminsn_type = 89;
pub const YARVINSN_opt_length: ruby_vminsn_type = 90; pub const YARVINSN_opt_aref_with: ruby_vminsn_type = 90;
pub const YARVINSN_opt_size: ruby_vminsn_type = 91; pub const YARVINSN_opt_length: ruby_vminsn_type = 91;
pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 92; pub const YARVINSN_opt_size: ruby_vminsn_type = 92;
pub const YARVINSN_opt_succ: ruby_vminsn_type = 93; pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 93;
pub const YARVINSN_opt_not: ruby_vminsn_type = 94; pub const YARVINSN_opt_succ: ruby_vminsn_type = 94;
pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 95; pub const YARVINSN_opt_not: ruby_vminsn_type = 95;
pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 96; pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 96;
pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 97; pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 97;
pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 98; pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 98;
pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 99; pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 99;
pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 100; pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 100;
pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 101; pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 101;
pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 102; pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 102;
pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 103; pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 103;
pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 104; pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 104;
pub const YARVINSN_trace_nop: ruby_vminsn_type = 105; pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 105;
pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 106; pub const YARVINSN_trace_nop: ruby_vminsn_type = 106;
pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 107; pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 107;
pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 108; pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 108;
pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 109; pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 109;
pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 110; pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 110;
pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 111; pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 111;
pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 112; pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 112;
pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 113; pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 113;
pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 114; pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 114;
pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 115; pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 115;
pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 116; pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 116;
pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 117; pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 117;
pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 118; pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 118;
pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 119; pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 119;
pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 120; pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 120;
pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 121; pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 121;
pub const YARVINSN_trace_putnil: ruby_vminsn_type = 122; pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 122;
pub const YARVINSN_trace_putself: ruby_vminsn_type = 123; pub const YARVINSN_trace_putnil: ruby_vminsn_type = 123;
pub const YARVINSN_trace_putobject: ruby_vminsn_type = 124; pub const YARVINSN_trace_putself: ruby_vminsn_type = 124;
pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 125; pub const YARVINSN_trace_putobject: ruby_vminsn_type = 125;
pub const YARVINSN_trace_putstring: ruby_vminsn_type = 126; pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 126;
pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 127; pub const YARVINSN_trace_putstring: ruby_vminsn_type = 127;
pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 128; pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 128;
pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 129; pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 129;
pub const YARVINSN_trace_intern: ruby_vminsn_type = 130; pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 130;
pub const YARVINSN_trace_newarray: ruby_vminsn_type = 131; pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 131;
pub const YARVINSN_trace_newarraykwsplat: ruby_vminsn_type = 132; pub const YARVINSN_trace_intern: ruby_vminsn_type = 132;
pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 133; pub const YARVINSN_trace_newarray: ruby_vminsn_type = 133;
pub const YARVINSN_trace_duparray: ruby_vminsn_type = 134; pub const YARVINSN_trace_newarraykwsplat: ruby_vminsn_type = 134;
pub const YARVINSN_trace_duphash: ruby_vminsn_type = 135; pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 135;
pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 136; pub const YARVINSN_trace_duparray: ruby_vminsn_type = 136;
pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 137; pub const YARVINSN_trace_duphash: ruby_vminsn_type = 137;
pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 138; pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 138;
pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 139; pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 139;
pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 140; pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 140;
pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 141; pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 141;
pub const YARVINSN_trace_newhash: ruby_vminsn_type = 142; pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 142;
pub const YARVINSN_trace_newrange: ruby_vminsn_type = 143; pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 143;
pub const YARVINSN_trace_pop: ruby_vminsn_type = 144; pub const YARVINSN_trace_newhash: ruby_vminsn_type = 144;
pub const YARVINSN_trace_dup: ruby_vminsn_type = 145; pub const YARVINSN_trace_newrange: ruby_vminsn_type = 145;
pub const YARVINSN_trace_dupn: ruby_vminsn_type = 146; pub const YARVINSN_trace_pop: ruby_vminsn_type = 146;
pub const YARVINSN_trace_swap: ruby_vminsn_type = 147; pub const YARVINSN_trace_dup: ruby_vminsn_type = 147;
pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 148; pub const YARVINSN_trace_dupn: ruby_vminsn_type = 148;
pub const YARVINSN_trace_topn: ruby_vminsn_type = 149; pub const YARVINSN_trace_swap: ruby_vminsn_type = 149;
pub const YARVINSN_trace_setn: ruby_vminsn_type = 150; pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 150;
pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 151; pub const YARVINSN_trace_topn: ruby_vminsn_type = 151;
pub const YARVINSN_trace_defined: ruby_vminsn_type = 152; pub const YARVINSN_trace_setn: ruby_vminsn_type = 152;
pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 153; pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 153;
pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 154; pub const YARVINSN_trace_defined: ruby_vminsn_type = 154;
pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 155; pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 155;
pub const YARVINSN_trace_checktype: ruby_vminsn_type = 156; pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 156;
pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 157; pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 157;
pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 158; pub const YARVINSN_trace_checktype: ruby_vminsn_type = 158;
pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 159; pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 159;
pub const YARVINSN_trace_send: ruby_vminsn_type = 160; pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 160;
pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 161; pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 161;
pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 162; pub const YARVINSN_trace_send: ruby_vminsn_type = 162;
pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 163; pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 163;
pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 164; pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 164;
pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 165; pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 165;
pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 166; pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 166;
pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 167; pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 167;
pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 168; pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 168;
pub const YARVINSN_trace_leave: ruby_vminsn_type = 169; pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 169;
pub const YARVINSN_trace_throw: ruby_vminsn_type = 170; pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 170;
pub const YARVINSN_trace_jump: ruby_vminsn_type = 171; pub const YARVINSN_trace_leave: ruby_vminsn_type = 171;
pub const YARVINSN_trace_branchif: ruby_vminsn_type = 172; pub const YARVINSN_trace_throw: ruby_vminsn_type = 172;
pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 173; pub const YARVINSN_trace_jump: ruby_vminsn_type = 173;
pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 174; pub const YARVINSN_trace_branchif: ruby_vminsn_type = 174;
pub const YARVINSN_trace_once: ruby_vminsn_type = 175; pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 175;
pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 176; pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 176;
pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 177; pub const YARVINSN_trace_once: ruby_vminsn_type = 177;
pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 178; pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 178;
pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 179; pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 179;
pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 180; pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 180;
pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 181; pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 181;
pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 182; pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 182;
pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 183; pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 183;
pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 184; pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 184;
pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 185; pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 185;
pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 186; pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 186;
pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 187; pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 187;
pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 188; pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 188;
pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 189; pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 189;
pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 190; pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 190;
pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 191; pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 191;
pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 192; pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 192;
pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 193; pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 193;
pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 194; pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 194;
pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 195; pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 195;
pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 196; pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 196;
pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 197; pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 197;
pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 198; pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 198;
pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 199; pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 199;
pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 200; pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 200;
pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 201; pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 201;
pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 202; pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 202;
pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 203; pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 203;
pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 204; pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 204;
pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 205; pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 205;
pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 206; pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 206;
pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 207; pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 207;
pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 208; pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 208;
pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 209; pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 209;
pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 210; pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 210;
pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 211;
pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 212;
pub type ruby_vminsn_type = u32; pub type ruby_vminsn_type = u32;
pub type rb_iseq_callback = ::std::option::Option< pub type rb_iseq_callback = ::std::option::Option<
unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void),
@ -1055,7 +1057,11 @@ extern "C" {
pub fn rb_str_byte_substr(str_: VALUE, beg: VALUE, len: VALUE) -> VALUE; pub fn rb_str_byte_substr(str_: VALUE, beg: VALUE, len: VALUE) -> VALUE;
pub fn rb_obj_as_string_result(str_: VALUE, obj: VALUE) -> VALUE; pub fn rb_obj_as_string_result(str_: VALUE, obj: VALUE) -> VALUE;
pub fn rb_str_concat_literals(num: usize, strary: *const VALUE) -> VALUE; pub fn rb_str_concat_literals(num: usize, strary: *const VALUE) -> VALUE;
pub fn rb_ec_str_resurrect(ec: *mut rb_execution_context_struct, str_: VALUE) -> VALUE; pub fn rb_ec_str_resurrect(
ec: *mut rb_execution_context_struct,
str_: VALUE,
chilled: bool,
) -> VALUE;
pub fn rb_to_hash_type(obj: VALUE) -> VALUE; pub fn rb_to_hash_type(obj: VALUE) -> VALUE;
pub fn rb_hash_stlike_foreach( pub fn rb_hash_stlike_foreach(
hash: VALUE, hash: VALUE,