Project

General

Profile

Misc #18354 ยป 0001-Lazily-create-singletons-on-instance_-exec-eval.patch

jhawthorn (John Hawthorn), 11/20/2021 11:50 PM

View differences:

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");
    (1-1/1)