class_eval vs instance_eval

class-eval, instance-eval, metaprogramming, ruby

Solution

Long story short:

- `Object.instance_eval &block` sets:

- `self` to `Object`

- The "current class" to `Object.singleton_class`

- `Object.class_eval &block` sets:

- `self` to `Object`

- The "current class" to `Object`

The "current class" is used for `def`, `undef` and `alias`, as well as constant and class variable lookups.

Now, let's have a look at the implementation details.

Here's how `module_eval` and `instance_eval` are implemented in C:

VALUE rb_mod_module_eval(int argc, VALUE *argv, VALUE mod) {
    return specific_eval(argc, argv, mod, mod);
}

VALUE rb_obj_instance_eval(int argc, VALUE *argv, VALUE self) {
    VALUE klass;
    if (SPECIAL_CONST_P(self)) { klass = Qnil; }
    else { klass = rb_singleton_class(self); }
    return specific_eval(argc, argv, klass, self);
}

Both call `specific_eval`, which takes the following arguments: `int argc`, `VALUE *argv`, `VALUE klass` and `VALUE self`.

Note that:

- `module_eval` passes the `Module` or `Class` instance as both `klass` and `self`

- `instance_eval` passes the object's singleton class as `klass`

If given a block, `specific_eval` will call `yield_under`, which takes the following arguments: `VALUE under`, `VALUE self` and `VALUE values`.

if (rb_block_given_p()) {
    rb_check_arity(argc, 0, 0);
    return yield_under(klass, self, Qundef);
}

There are two important lines in `yield_under`:

`block.self = self;`

This sets the `self` of the block to the receiver.

`cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);`

The `cref` is a linked list which specifies the "current class", which is used for `def`, `undef` and `alias`, as well as constant and class variable lookups.

That line basically sets the `cref` to `under`.

Finally:

When called from `module_eval`, `under` will be the `Class` or `Module` instance.

When called from `instance_eval`, `under` will be the singleton class of `self`.

Problem

Is there any difference in how `class_eval` & `instance_eval` work except `def`? Inside `class_eval` block `def` defines method to class itself (i.e. instance method) and inside `instance_eval` `def` defines method to the eigenclass of the class (i.e. class method). AFAIK all other features work identically in both cases (e.g. `define_method`, `attr_accessor`, `class << self; end`, defining constants). Is it true? Answer is: `def`, `undef` and `alias` have different contexts for `class_eval` and `instance_eval`.

Original source