Misc #18354 ยป 0001-Lazily-create-singletons-on-instance_-exec-eval.patch
bootstraptest/test_eval.rb | ||
---|---|---|
Const
|
||
}
|
||
}
|
||
assert_equal %q{1}, %q{
|
||
class TrueClass
|
||
Const = 1
|
||
end
|
||
true.instance_eval %{
|
||
Const
|
||
}
|
||
}
|
||
assert_equal %q{[:Const]}, %q{
|
||
mod = Module.new
|
||
mod.instance_eval %{
|
||
Const = 1
|
||
}
|
||
raise if defined?(Module::Const)
|
||
mod.singleton_class.constants
|
||
}
|
||
assert_equal %q{top}, %q{
|
||
Const = :top
|
||
class C
|
eval_intern.h | ||
---|---|---|
#define CREF_FL_PUSHED_BY_EVAL IMEMO_FL_USER1
|
||
#define CREF_FL_OMOD_SHARED IMEMO_FL_USER2
|
||
#define CREF_FL_SINGLETON IMEMO_FL_USER3
|
||
static inline int CREF_SINGLETON(const rb_cref_t *cref);
|
||
static inline VALUE
|
||
CREF_CLASS(const rb_cref_t *cref)
|
||
{
|
||
return cref->klass;
|
||
if (CREF_SINGLETON(cref)) {
|
||
return CLASS_OF(cref->klass_or_self);
|
||
} else {
|
||
return cref->klass_or_self;
|
||
}
|
||
}
|
||
static inline VALUE
|
||
CREF_CLASS_FOR_DEFINITION(const rb_cref_t *cref)
|
||
{
|
||
if (CREF_SINGLETON(cref)) {
|
||
return rb_singleton_class(cref->klass_or_self);
|
||
} else {
|
||
return cref->klass_or_self;
|
||
}
|
||
}
|
||
static inline rb_cref_t *
|
||
... | ... | |
cref->flags |= CREF_FL_PUSHED_BY_EVAL;
|
||
}
|
||
static inline int
|
||
CREF_SINGLETON(const rb_cref_t *cref)
|
||
{
|
||
return cref->flags & CREF_FL_SINGLETON;
|
||
}
|
||
static inline void
|
||
CREF_SINGLETON_SET(rb_cref_t *cref)
|
||
{
|
||
cref->flags |= CREF_FL_SINGLETON;
|
||
}
|
||
static inline int
|
||
CREF_OMOD_SHARED(const rb_cref_t *cref)
|
||
{
|
gc.c | ||
---|---|---|
}
|
||
return;
|
||
case imemo_cref:
|
||
gc_mark(objspace, RANY(obj)->as.imemo.cref.klass);
|
||
gc_mark(objspace, RANY(obj)->as.imemo.cref.klass_or_self);
|
||
gc_mark(objspace, (VALUE)RANY(obj)->as.imemo.cref.next);
|
||
gc_mark(objspace, RANY(obj)->as.imemo.cref.refinements);
|
||
return;
|
||
... | ... | |
}
|
||
break;
|
||
case imemo_cref:
|
||
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.cref.klass);
|
||
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.cref.klass_or_self);
|
||
TYPED_UPDATE_IF_MOVED(objspace, struct rb_cref_struct *, RANY(obj)->as.imemo.cref.next);
|
||
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.cref.refinements);
|
||
break;
|
insns.def | ||
---|---|---|
(rb_num_t value_type)
|
||
()
|
||
(VALUE val)
|
||
// attr bool leaf = (value_type != VM_SPECIAL_OBJECT_CBASE); /* get cbase may allocate a singleton */
|
||
{
|
||
enum vm_special_object_type type;
|
||
method.h | ||
---|---|---|
typedef struct rb_cref_struct {
|
||
VALUE flags;
|
||
VALUE refinements;
|
||
VALUE klass;
|
||
VALUE klass_or_self;
|
||
struct rb_cref_struct * next;
|
||
const rb_scope_visibility_t scope_visi;
|
||
} rb_cref_t;
|
test/ruby/test_eval.rb | ||
---|---|---|
end
|
||
end
|
||
def test_instance_exec_cvar
|
||
[Object.new, [], 7, :sym, true, false, nil].each do |obj|
|
||
assert_equal(13, obj.instance_exec{@@cvar})
|
||
end
|
||
end
|
||
def test_instance_eval_method
|
||
bug2788 = '[ruby-core:28324]'
|
||
[Object.new, [], nil, true, false].each do |o|
|
||
... | ... | |
assert_equal(2, bar)
|
||
end
|
||
def test_instance_exec_block_basic
|
||
forall_TYPE do |o|
|
||
assert_equal nil, o.instance_exec { nil }
|
||
assert_equal true, o.instance_exec { true }
|
||
assert_equal false, o.instance_exec { false }
|
||
assert_equal o, o.instance_exec { self }
|
||
assert_equal 1, o.instance_exec { 1 }
|
||
assert_equal :sym, o.instance_exec { :sym }
|
||
assert_equal 11, o.instance_exec { 11 }
|
||
assert_equal 12, o.instance_exec { @ivar } unless o.frozen?
|
||
assert_equal 13, o.instance_exec { @@cvar }
|
||
assert_equal 14, o.instance_exec { $gvar__eval }
|
||
assert_equal 15, o.instance_exec { Const }
|
||
assert_equal 16, o.instance_exec { 7 + 9 }
|
||
assert_equal 17, o.instance_exec { 17.to_i }
|
||
assert_equal "18", o.instance_exec { "18" }
|
||
assert_equal "19", o.instance_exec { "1#{9}" }
|
||
1.times {
|
||
assert_equal 12, o.instance_exec { @ivar } unless o.frozen?
|
||
assert_equal 13, o.instance_exec { @@cvar }
|
||
assert_equal 14, o.instance_exec { $gvar__eval }
|
||
assert_equal 15, o.instance_exec { Const }
|
||
}
|
||
end
|
||
end
|
||
def test_instance_exec_method_definition
|
||
klass = Class.new
|
||
o = klass.new
|
||
o.instance_exec do
|
||
def foo
|
||
:foo_result
|
||
end
|
||
end
|
||
assert_respond_to o, :foo
|
||
refute_respond_to klass, :foo
|
||
refute_respond_to klass.new, :foo
|
||
assert_equal :foo_result, o.foo
|
||
end
|
||
def test_instance_exec_eval_method_definition
|
||
klass = Class.new
|
||
o = klass.new
|
||
o.instance_exec do
|
||
eval %{
|
||
def foo
|
||
:foo_result
|
||
end
|
||
}
|
||
end
|
||
assert_respond_to o, :foo
|
||
refute_respond_to klass, :foo
|
||
refute_respond_to klass.new, :foo
|
||
assert_equal :foo_result, o.foo
|
||
end
|
||
#
|
||
# From ruby/test/ruby/test_eval.rb
|
||
#
|
test/ruby/test_undef.rb | ||
---|---|---|
end
|
||
end
|
||
end
|
||
def test_singleton_undef
|
||
klass = Class.new do
|
||
def foo
|
||
:ok
|
||
end
|
||
end
|
||
klass.new.foo
|
||
klass.new.instance_eval do
|
||
undef foo
|
||
end
|
||
klass.new.foo
|
||
end
|
||
end
|
vm.c | ||
---|---|---|
}
|
||
}
|
||
VM_ASSERT(klass);
|
||
cref = (rb_cref_t *)rb_imemo_new(imemo_cref, klass, (VALUE)(use_prev_prev ? CREF_NEXT(prev_cref) : prev_cref), scope_visi.value, refinements);
|
||
if (pushed_by_eval) CREF_PUSHED_BY_EVAL_SET(cref);
|
||
... | ... | |
static rb_cref_t *
|
||
vm_cref_dup(const rb_cref_t *cref)
|
||
{
|
||
VALUE klass = CREF_CLASS(cref);
|
||
const rb_scope_visibility_t *visi = CREF_SCOPE_VISI(cref);
|
||
rb_cref_t *next_cref = CREF_NEXT(cref), *new_cref;
|
||
int pushed_by_eval = CREF_PUSHED_BY_EVAL(cref);
|
||
new_cref = vm_cref_new(klass, visi->method_visi, visi->module_func, next_cref, pushed_by_eval);
|
||
new_cref = vm_cref_new(cref->klass_or_self, visi->method_visi, visi->module_func, next_cref, pushed_by_eval);
|
||
if (!NIL_P(CREF_REFINEMENTS(cref))) {
|
||
VALUE ref = rb_hash_dup(CREF_REFINEMENTS(cref));
|
||
rb_hash_foreach(ref, ref_delete_symkey, Qnil);
|
||
CREF_REFINEMENTS_SET(new_cref, ref);
|
||
CREF_OMOD_SHARED_UNSET(new_cref);
|
||
CREF_OMOD_SHARED_UNSET(new_cref);
|
||
}
|
||
if (CREF_SINGLETON(cref)) {
|
||
CREF_SINGLETON_SET(new_cref);
|
||
}
|
||
return new_cref;
|
vm_eval.c | ||
---|---|---|
{
|
||
const struct eval_string_wrap_arg *const arg = (struct eval_string_wrap_arg*)data;
|
||
rb_cref_t *cref = rb_vm_cref_new_toplevel();
|
||
cref->klass = arg->klass;
|
||
cref->klass_or_self = arg->klass;
|
||
return eval_string_with_cref(arg->top_self, rb_str_new_cstr(arg->str), cref, rb_str_new_cstr("eval"), 1);
|
||
}
|
||
... | ... | |
/* block eval under the class/module context */
|
||
static VALUE
|
||
yield_under(VALUE under, VALUE self, int argc, const VALUE *argv, int kw_splat)
|
||
yield_under(VALUE under, int singleton, VALUE self, int argc, const VALUE *argv, int kw_splat)
|
||
{
|
||
rb_execution_context_t *ec = GET_EC();
|
||
rb_control_frame_t *cfp = ec->cfp;
|
||
... | ... | |
VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(ec->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
|
||
}
|
||
//rb_obj_info_dump(under);
|
||
// Make crefs log that this is a special lazy singleton?
|
||
cref = vm_cref_push(ec, under, ep, TRUE);
|
||
if (singleton) {
|
||
cref->klass_or_self = self;
|
||
CREF_SINGLETON_SET(cref);
|
||
}
|
||
return vm_yield_with_cref(ec, argc, argv, kw_splat, cref, is_lambda);
|
||
}
|
||
... | ... | |
/* string eval under the class/module context */
|
||
static VALUE
|
||
eval_under(VALUE under, VALUE self, VALUE src, VALUE file, int line)
|
||
eval_under(VALUE under, int singleton, VALUE self, VALUE src, VALUE file, int line)
|
||
{
|
||
rb_cref_t *cref = vm_cref_push(GET_EC(), under, NULL, SPECIAL_CONST_P(self) && !NIL_P(under));
|
||
rb_cref_t *cref = vm_cref_push(GET_EC(), under, NULL, FALSE);
|
||
if (singleton) {
|
||
cref->klass_or_self = self;
|
||
CREF_SINGLETON_SET(cref);
|
||
}
|
||
SafeStringValue(src);
|
||
return eval_string_with_cref(self, src, cref, file, line);
|
||
}
|
||
static VALUE
|
||
specific_eval(int argc, const VALUE *argv, VALUE klass, VALUE self, int kw_splat)
|
||
specific_eval(int argc, const VALUE *argv, VALUE klass, VALUE self, int singleton, int kw_splat)
|
||
{
|
||
if (rb_block_given_p()) {
|
||
rb_check_arity(argc, 0, 0);
|
||
return yield_under(klass, self, 1, &self, kw_splat);
|
||
return yield_under(klass, singleton, self, 1, &self, kw_splat);
|
||
}
|
||
else {
|
||
VALUE file = Qundef;
|
||
... | ... | |
file = argv[1];
|
||
if (!NIL_P(file)) StringValue(file);
|
||
}
|
||
return eval_under(klass, self, code, file, line);
|
||
}
|
||
}
|
||
static VALUE
|
||
singleton_class_for_eval(VALUE self)
|
||
{
|
||
if (SPECIAL_CONST_P(self)) {
|
||
return rb_special_singleton_class(self);
|
||
}
|
||
switch (BUILTIN_TYPE(self)) {
|
||
case T_FLOAT: case T_BIGNUM: case T_SYMBOL:
|
||
return Qnil;
|
||
case T_STRING:
|
||
if (FL_TEST_RAW(self, RSTRING_FSTR)) return Qnil;
|
||
default:
|
||
return rb_singleton_class(self);
|
||
return eval_under(klass, singleton, self, code, file, line);
|
||
}
|
||
}
|
||
... | ... | |
static VALUE
|
||
rb_obj_instance_eval_internal(int argc, const VALUE *argv, VALUE self)
|
||
{
|
||
VALUE klass = singleton_class_for_eval(self);
|
||
return specific_eval(argc, argv, klass, self, RB_PASS_CALLED_KEYWORDS);
|
||
VALUE klass = CLASS_OF(self);
|
||
return specific_eval(argc, argv, klass, self, TRUE, RB_PASS_CALLED_KEYWORDS);
|
||
}
|
||
VALUE
|
||
rb_obj_instance_eval(int argc, const VALUE *argv, VALUE self)
|
||
{
|
||
VALUE klass = singleton_class_for_eval(self);
|
||
return specific_eval(argc, argv, klass, self, RB_NO_KEYWORDS);
|
||
VALUE klass = CLASS_OF(self);
|
||
return specific_eval(argc, argv, klass, self, TRUE, RB_NO_KEYWORDS);
|
||
}
|
||
/*
|
||
... | ... | |
static VALUE
|
||
rb_obj_instance_exec_internal(int argc, const VALUE *argv, VALUE self)
|
||
{
|
||
VALUE klass = singleton_class_for_eval(self);
|
||
return yield_under(klass, self, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
||
VALUE klass = CLASS_OF(self);
|
||
return yield_under(klass, TRUE, self, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
||
}
|
||
VALUE
|
||
rb_obj_instance_exec(int argc, const VALUE *argv, VALUE self)
|
||
{
|
||
VALUE klass = singleton_class_for_eval(self);
|
||
return yield_under(klass, self, argc, argv, RB_NO_KEYWORDS);
|
||
VALUE klass = CLASS_OF(self);
|
||
return yield_under(klass, TRUE, self, argc, argv, RB_NO_KEYWORDS);
|
||
}
|
||
/*
|
||
... | ... | |
static VALUE
|
||
rb_mod_module_eval_internal(int argc, const VALUE *argv, VALUE mod)
|
||
{
|
||
return specific_eval(argc, argv, mod, mod, RB_PASS_CALLED_KEYWORDS);
|
||
return specific_eval(argc, argv, mod, mod, FALSE, RB_PASS_CALLED_KEYWORDS);
|
||
}
|
||
VALUE
|
||
rb_mod_module_eval(int argc, const VALUE *argv, VALUE mod)
|
||
{
|
||
return specific_eval(argc, argv, mod, mod, RB_NO_KEYWORDS);
|
||
return specific_eval(argc, argv, mod, mod, FALSE, RB_NO_KEYWORDS);
|
||
}
|
||
/*
|
||
... | ... | |
static VALUE
|
||
rb_mod_module_exec_internal(int argc, const VALUE *argv, VALUE mod)
|
||
{
|
||
return yield_under(mod, mod, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
||
return yield_under(mod, FALSE, mod, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
||
}
|
||
VALUE
|
||
rb_mod_module_exec(int argc, const VALUE *argv, VALUE mod)
|
||
{
|
||
return yield_under(mod, mod, argc, argv, RB_NO_KEYWORDS);
|
||
return yield_under(mod, FALSE, mod, argc, argv, RB_NO_KEYWORDS);
|
||
}
|
||
/*
|
vm_insnhelper.c | ||
---|---|---|
vm_get_cbase(const VALUE *ep)
|
||
{
|
||
const rb_cref_t *cref = vm_get_cref(ep);
|
||
VALUE klass = Qundef;
|
||
while (cref) {
|
||
if ((klass = CREF_CLASS(cref)) != 0) {
|
||
break;
|
||
}
|
||
cref = CREF_NEXT(cref);
|
||
}
|
||
VALUE klass = CREF_CLASS_FOR_DEFINITION(cref);
|
||
return klass;
|
||
}
|
||
... | ... | |
while (cref) {
|
||
if (!CREF_PUSHED_BY_EVAL(cref) &&
|
||
(klass = CREF_CLASS(cref)) != 0) {
|
||
(klass = CREF_CLASS_FOR_DEFINITION(cref)) != 0) {
|
||
break;
|
||
}
|
||
cref = CREF_NEXT(cref);
|
||
... | ... | |
while (CREF_NEXT(cref) &&
|
||
(NIL_P(CREF_CLASS(cref)) || FL_TEST(CREF_CLASS(cref), FL_SINGLETON) ||
|
||
CREF_PUSHED_BY_EVAL(cref))) {
|
||
CREF_PUSHED_BY_EVAL(cref) || CREF_SINGLETON(cref))) {
|
||
cref = CREF_NEXT(cref);
|
||
}
|
||
if (top_level_raise && !CREF_NEXT(cref)) {
|
||
... | ... | |
rb_method_visibility_t visi;
|
||
rb_cref_t *cref = vm_ec_cref(ec);
|
||
if (!is_singleton) {
|
||
klass = CREF_CLASS(cref);
|
||
visi = vm_scope_visibility_get(ec);
|
||
}
|
||
else { /* singleton */
|
||
if (is_singleton) {
|
||
klass = rb_singleton_class(obj); /* class and frozen checked in this API */
|
||
visi = METHOD_VISI_PUBLIC;
|
||
}
|
||
else {
|
||
klass = CREF_CLASS_FOR_DEFINITION(cref);
|
||
visi = vm_scope_visibility_get(ec);
|
||
}
|
||
if (NIL_P(klass)) {
|
||
rb_raise(rb_eTypeError, "no class/module to add method");
|