refinements in string interpolation

* compile.c (iseq_compile_each0): insert to_s method call, so that
  refinements activated at the caller should take place.
  [Feature #13812]

* insns.def (tostring): fix up converted object to a string,
  infect and fallback.

* insns.def (branchiftype): new instruction for conversion.
  branches if TOS is an instance of the given type.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59950 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2017-09-18 02:27:13 +00:00
parent d1e63172c6
commit b2da3824c5
5 changed files with 51 additions and 2 deletions

2
NEWS
View File

@ -18,6 +18,8 @@ with all sufficient information, see the ChangeLog file or Redmine
* rescue/else/ensure are allowed inside do/end blocks. [Feature #12906] * rescue/else/ensure are allowed inside do/end blocks. [Feature #12906]
* refinements take place in string interpolations. [Feature #13812]
=== Core classes updates (outstanding ones only) === Core classes updates (outstanding ones only)
* Array * Array

View File

@ -6180,7 +6180,15 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
ADD_INSN(ret, line, pop); ADD_INSN(ret, line, pop);
} }
else { else {
const unsigned int flag = VM_CALL_FCALL;
LABEL *isstr = NEW_LABEL(line);
ADD_INSN(ret, line, dup);
ADD_INSN2(ret, line, branchiftype, INT2FIX(T_STRING), isstr);
LABEL_REF(isstr);
ADD_INSN(ret, line, dup);
ADD_SEND_R(ret, line, idTo_s, INT2FIX(0), NULL, INT2FIX(flag), NULL);
ADD_INSN(ret, line, tostring); ADD_INSN(ret, line, tostring);
ADD_LABEL(ret, isstr);
} }
break; break;
} }

View File

@ -356,10 +356,11 @@ concatstrings
DEFINE_INSN DEFINE_INSN
tostring tostring
() ()
(VALUE val) (VALUE val, VALUE str)
(VALUE val) (VALUE val)
{ {
val = rb_obj_as_string(val); VALUE rb_obj_as_string_result(VALUE str, VALUE obj);
val = rb_obj_as_string_result(str, val);
} }
/** /**
@ -1038,6 +1039,23 @@ branchnil
} }
} }
/**
@c jump
@e if val is type, set PC to (PC + dst).
@j val type PC (PC + dst)
*/
DEFINE_INSN
branchiftype
(rb_num_t type, OFFSET dst)
(VALUE val)
()
{
if (TYPE(val) == (int)type) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
/**********************************************************/ /**********************************************************/
/* for optimize */ /* for optimize */

View File

@ -1404,6 +1404,8 @@ str_shared_replace(VALUE str, VALUE str2)
} }
} }
VALUE rb_obj_as_string_result(VALUE str, VALUE obj);
VALUE VALUE
rb_obj_as_string(VALUE obj) rb_obj_as_string(VALUE obj)
{ {
@ -1413,6 +1415,12 @@ rb_obj_as_string(VALUE obj)
return obj; return obj;
} }
str = rb_funcall(obj, idTo_s, 0); str = rb_funcall(obj, idTo_s, 0);
return rb_obj_as_string_result(str, obj);
}
VALUE
rb_obj_as_string_result(VALUE str, VALUE obj)
{
if (!RB_TYPE_P(str, T_STRING)) if (!RB_TYPE_P(str, T_STRING))
return rb_any_to_s(obj); return rb_any_to_s(obj);
if (!FL_TEST_RAW(str, RSTRING_FSTR) && FL_ABLE(obj)) if (!FL_TEST_RAW(str, RSTRING_FSTR) && FL_ABLE(obj))

View File

@ -1961,6 +1961,19 @@ class TestRefinement < Test::Unit::TestCase
end end
end end
class ToString
c = self
using Module.new {refine(c) {def to_s; "ok"; end}}
def string
"#{self}"
end
end
def test_tostring
assert_equal("ok", ToString.new.string)
assert_predicate(ToString.new.taint.string, :tainted?)
end
private private
def eval_using(mod, s) def eval_using(mod, s)