Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@
# - #gsub!: Replaces each substring that matches a given pattern with a given replacement string;
# returns +self+ if any changes, +nil+ otherwise.
# - #succ! (aliased as #next!): Returns +self+ modified to become its own successor.
# - #initialize_copy (aliased as #replace): Returns +self+ with its entire content replaced by a given string.
# - #replace: Returns +self+ with its entire content replaced by a given string.
# - #reverse!: Returns +self+ with its characters in reverse order.
# - #setbyte: Sets the byte at a given integer offset to a given value; returns the argument.
# - #tr!: Replaces specified characters in +self+ with specified replacement characters;
Expand Down
6 changes: 6 additions & 0 deletions prism/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -5280,6 +5280,12 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_

switch (PM_NODE_TYPE(part)) {
case PM_STRING_NODE:
// If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
// because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
// as long as this interpolation only consists of other string literals.
if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
}
part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
break;
case PM_INTERPOLATED_STRING_NODE:
Expand Down
23 changes: 14 additions & 9 deletions prism_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ parse_regexp_concat(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm
static void pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node);

static int
pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool mutable_result)
pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool mutable_result, bool frozen_result)
{
int stack_size = 0;
size_t parts_size = parts->size;
Expand Down Expand Up @@ -668,10 +668,15 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const
if (RTEST(current_string)) {
current_string = rb_fstring(current_string);

if (stack_size == 0 && (interpolated || mutable_result)) {
PUSH_INSN1(ret, current_location, putstring, current_string);
}
else {
if (stack_size == 0) {
if (frozen_result) {
PUSH_INSN1(ret, current_location, putobject, current_string);
} else if (mutable_result || interpolated) {
PUSH_INSN1(ret, current_location, putstring, current_string);
} else {
PUSH_INSN1(ret, current_location, putchilledstring, current_string);
}
} else {
PUSH_INSN1(ret, current_location, putobject, current_string);
}

Expand All @@ -692,7 +697,7 @@ pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_
rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;

int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false, false);
PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
}

Expand Down Expand Up @@ -9571,7 +9576,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
}
else {
const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node;
int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, !PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN));
int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE), PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN));
if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
if (popped) PUSH_INSN(ret, location, pop);
}
Expand All @@ -9582,7 +9587,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
// :"foo #{bar}"
// ^^^^^^^^^^^^^
const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node;
int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, false);
int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, false, false);

if (length > 1) {
PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
Expand All @@ -9604,7 +9609,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,

PUSH_INSN(ret, location, putself);

int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL, false);
int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL, false, false);
if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));

PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
Expand Down
8 changes: 8 additions & 0 deletions spec/ruby/core/string/chilled_string_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@
input.should == "chilled-mutated"
end

it "emits a warning for concatenated strings" do
input = "still" "+chilled"
-> {
input << "-mutated"
}.should complain(/literal string will be frozen in the future/)
input.should == "still+chilled-mutated"
end

it "emits a warning on singleton_class creation" do
-> {
"chilled".singleton_class
Expand Down
14 changes: 9 additions & 5 deletions string.c
Original file line number Diff line number Diff line change
Expand Up @@ -6648,11 +6648,13 @@ rb_str_gsub(int argc, VALUE *argv, VALUE str)
* call-seq:
* replace(other_string) -> self
*
* Replaces the contents of +self+ with the contents of +other_string+:
* Replaces the contents of +self+ with the contents of +other_string+;
* returns +self+:
*
* s = 'foo' # => "foo"
* s.replace('bar') # => "bar"
*
* Related: see {Modifying}[rdoc-ref:String@Modifying].
*/

VALUE
Expand Down Expand Up @@ -7052,10 +7054,12 @@ rb_str_reverse(VALUE str)
*
* Returns +self+ with its characters reversed:
*
* s = 'stressed'
* s.reverse! # => "desserts"
* s # => "desserts"
* 'drawer'.reverse! # => "reward"
* 'reviled'.reverse! # => "deliver"
* 'stressed'.reverse! # => "desserts"
* 'semordnilaps'.reverse! # => "spalindromes"
*
* Related: see {Modifying}[rdoc-ref:String@Modifying].
*/

static VALUE
Expand Down Expand Up @@ -12818,6 +12822,7 @@ Init_String(void)
rb_define_singleton_method(rb_cString, "new", rb_str_s_new, -1);
rb_define_singleton_method(rb_cString, "try_convert", rb_str_s_try_convert, 1);
rb_define_method(rb_cString, "initialize", rb_str_init, -1);
rb_define_method(rb_cString, "replace", rb_str_replace, 1);
rb_define_method(rb_cString, "initialize_copy", rb_str_replace, 1);
rb_define_method(rb_cString, "<=>", rb_str_cmp_m, 1);
rb_define_method(rb_cString, "==", rb_str_equal, 1);
Expand Down Expand Up @@ -12848,7 +12853,6 @@ Init_String(void)
rb_define_method(rb_cString, "byteindex", rb_str_byteindex_m, -1);
rb_define_method(rb_cString, "rindex", rb_str_rindex_m, -1);
rb_define_method(rb_cString, "byterindex", rb_str_byterindex_m, -1);
rb_define_method(rb_cString, "replace", rb_str_replace, 1);
rb_define_method(rb_cString, "clear", rb_str_clear, 0);
rb_define_method(rb_cString, "chr", rb_str_chr, 0);
rb_define_method(rb_cString, "getbyte", rb_str_getbyte, 1);
Expand Down
8 changes: 4 additions & 4 deletions zjit/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::cruby::*;
use std::collections::HashSet;

/// Default --zjit-num-profiles
const DEFAULT_NUM_PROFILES: u8 = 5;
const DEFAULT_NUM_PROFILES: u32 = 5;

/// Default --zjit-call-threshold. This should be large enough to avoid compiling
/// warmup code, but small enough to perform well on micro-benchmarks.
Expand Down Expand Up @@ -40,7 +40,7 @@ pub struct Options {
pub mem_bytes: usize,

/// Number of times YARV instructions should be profiled.
pub num_profiles: u8,
pub num_profiles: u32,

/// Enable YJIT statsitics
pub stats: bool,
Expand Down Expand Up @@ -112,9 +112,9 @@ pub const ZJIT_OPTIONS: &[(&str, &str)] = &[
("--zjit-mem-size=num",
"Max amount of memory that ZJIT can use (in MiB)."),
("--zjit-call-threshold=num",
"Number of calls to trigger JIT (default: 2)."),
"Number of calls to trigger JIT (default: 30)."),
("--zjit-num-profiles=num",
"Number of profiled calls before JIT (default: 1, max: 255)."),
"Number of profiled calls before JIT (default: 5)."),
("--zjit-stats[=quiet]", "Enable collecting ZJIT statistics (=quiet to suppress output)."),
("--zjit-perf", "Dump ISEQ symbols into /tmp/perf-{}.map for Linux perf."),
("--zjit-log-compiled-iseqs=path",
Expand Down
2 changes: 1 addition & 1 deletion zjit/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ pub struct IseqProfile {
opnd_types: Vec<Vec<TypeDistribution>>,

/// Number of profiled executions for each YARV instruction, indexed by the instruction index
num_profiles: Vec<u8>,
num_profiles: Vec<u32>,
}

impl IseqProfile {
Expand Down