diff --git a/bootstraptest/test_method.rb b/bootstraptest/test_method.rb
index 8d0db6e..c6c3b09 100644
--- a/bootstraptest/test_method.rb
+++ b/bootstraptest/test_method.rb
@@ -287,16 +287,17 @@ assert_equal '1',       %q( class C; def m() 1 end end
 assert_equal '1',       %q( class C
                               def m
                                 def mm() 1 end
+                                mm
                               end
                             end
-                            C.new.m
-                            C.new.mm )
+                            C.new.m )
 assert_equal '1',       %q( class C
                               def m
                                 def mm() 1 end
+                                mm
                               end
                             end
-                            instance_eval "C.new.m; C.new.mm" )
+                            instance_eval "C.new.m" )
 
 # method_missing
 assert_equal ':m',      %q( class C
diff --git a/compile.c b/compile.c
index b9749f4..8875e57 100644
--- a/compile.c
+++ b/compile.c
@@ -4575,7 +4575,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 	ADD_INSN1(ret, nd_line(node), putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
 	ADD_INSN1(ret, nd_line(node), putobject, ID2SYM(node->nd_mid));
 	ADD_INSN1(ret, nd_line(node), putiseq, iseqval);
-	ADD_SEND (ret, nd_line(node), ID2SYM(id_core_define_method), INT2FIX(3));
+	ADD_INSN1(ret, nd_line(node), putobject,
+		  node->flags & NODE_FL_NESTED_DEF ? Qtrue : Qfalse);
+	ADD_SEND (ret, nd_line(node), ID2SYM(id_core_define_method), INT2FIX(4));
 
 	if (poped) {
 	    ADD_INSN(ret, nd_line(node), pop);
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 1d2546a..4d39f37 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -630,6 +630,7 @@ struct RClass {
 #define RMODULE_M_TBL(m) RCLASS_M_TBL(m)
 #define RMODULE_SUPER(m) RCLASS_SUPER(m)
 #define RMODULE_IS_OVERLAYED FL_USER2
+#define RMODULE_HAS_NESTED_METHODS FL_USER3
 
 struct RFloat {
     struct RBasic basic;
diff --git a/node.h b/node.h
index b76e141..02e1fb5 100644
--- a/node.h
+++ b/node.h
@@ -269,6 +269,7 @@ typedef struct RNode {
 #define NODE_FL_NEWLINE (((VALUE)1)<<7)
 #define NODE_FL_CREF_PUSHED_BY_EVAL NODE_FL_NEWLINE
 #define NODE_FL_CREF_OMOD_SHARED (((VALUE)1)<<6)
+#define NODE_FL_NESTED_DEF NODE_FL_CREF_OMOD_SHARED
 
 #define NODE_TYPESHIFT 8
 #define NODE_TYPEMASK  (((VALUE)0x7f)<<NODE_TYPESHIFT)
diff --git a/parse.y b/parse.y
index 13e18f2..67ca1f1 100644
--- a/parse.y
+++ b/parse.y
@@ -2967,6 +2967,8 @@ primary		: literal
 			reduce_nodes(&body);
 			$$ = NEW_DEFN($2, $4, body, NOEX_PRIVATE);
 			nd_set_line($$, $<num>1);
+			if (in_def > 1 || in_single > 0)
+			    $$->flags |= NODE_FL_NESTED_DEF;
 		    /*%
 			$$ = dispatch3(def, $2, $4, $5);
 		    %*/
diff --git a/test/rdoc/test_rdoc_text.rb b/test/rdoc/test_rdoc_text.rb
index 7e0f2cf..3b4b78e 100644
--- a/test/rdoc/test_rdoc_text.rb
+++ b/test/rdoc/test_rdoc_text.rb
@@ -62,8 +62,8 @@ The comments associated with
     assert_equal expected, flush_left(text)
   end
 
+  def formatter() RDoc::Markup::ToHtml.new end
   def test_markup
-    def formatter() RDoc::Markup::ToHtml.new end
 
     assert_equal "<p>\nhi\n</p>\n", markup('hi')
   end
diff --git a/test/ruby/test_nested_method.rb b/test/ruby/test_nested_method.rb
new file mode 100644
index 0000000..2b22fd4
--- /dev/null
+++ b/test/ruby/test_nested_method.rb
@@ -0,0 +1,29 @@
+require 'test/unit'
+
+class TestNestedMethod < Test::Unit::TestCase
+  def call_nested_method
+    def foo
+      return "foo"
+    end
+
+    return foo
+  end
+
+  def test_nested_method
+    assert_equal("foo", call_nested_method)
+    assert_raise(NoMethodError) { foo() }
+  end
+
+  def test_doubly_nested_method
+    def call_doubly_nested_method
+      def foo
+        return "foo"
+      end
+
+      return foo
+    end
+
+    assert_equal("foo", call_doubly_nested_method)
+    assert_raise(NoMethodError) { foo() }
+  end
+end
diff --git a/vm.c b/vm.c
index bf445c8..b77330b 100644
--- a/vm.c
+++ b/vm.c
@@ -1822,14 +1822,37 @@ rb_thread_alloc(VALUE klass)
     return self;
 }
 
+static VALUE
+find_module_for_nested_methods(NODE *cref, VALUE klass)
+{
+    VALUE iclass;
+
+    if (NIL_P(cref->nd_omod))
+	return Qnil;
+    iclass = rb_hash_lookup(cref->nd_omod, klass);
+    if (NIL_P(iclass))
+	return Qnil;
+    while (iclass) {
+	VALUE module = RBASIC(iclass)->klass;
+	if (FL_TEST(module, RMODULE_HAS_NESTED_METHODS)) {
+	    return module;
+	}
+	iclass = RCLASS_SUPER(iclass);
+    }
+    return Qnil;
+}
+
 VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
 
 static void
-vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
+vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, VALUE nested,
 		 rb_num_t is_singleton, NODE *cref)
 {
     VALUE klass = cref->nd_clss;
+    VALUE target, defined_class;
+    rb_method_entry_t *me;
     int noex = (int)cref->nd_visi;
+    int is_nested = RTEST(nested);
     rb_iseq_t *miseq;
     GetISeqPtr(iseqval, miseq);
 
@@ -1854,12 +1877,43 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
 	noex = NOEX_PUBLIC;
     }
 
+    if (is_nested && th->cfp->lfp == th->cfp->dfp) {
+	VALUE c;
+       	if (TYPE(klass) == T_MODULE) {
+	    c = rb_obj_class(th->cfp->self);
+	}
+	else {
+	    c = klass;
+	}
+	if (cref->flags & NODE_FL_CREF_OMOD_SHARED) {
+	    target = Qnil;
+	}
+	else {
+	    target = find_module_for_nested_methods(cref, c);
+	}
+	if (NIL_P(target)) {
+	    target = rb_module_new();
+	    FL_SET(target, RMODULE_HAS_NESTED_METHODS);
+	    rb_overlay_module(cref, c, target);
+	}
+	else {
+	    me = search_method(target, id, Qnil, &defined_class);
+	    if (me && me->def->type == VM_METHOD_TYPE_ISEQ &&
+		me->def->body.iseq == miseq) {
+		return;
+	    }
+	}
+	noex = NOEX_PRIVATE;
+    }
+    else {
+	target = klass;
+    }
     /* dup */
     COPY_CREF(miseq->cref_stack, cref);
     miseq->cref_stack->nd_visi = NOEX_PUBLIC;
-    miseq->klass = klass;
+    miseq->klass = target;
     miseq->defined_method_id = id;
-    rb_add_method(klass, id, VM_METHOD_TYPE_ISEQ, miseq, noex);
+    rb_add_method(target, id, VM_METHOD_TYPE_ISEQ, miseq, noex);
 
     if (!is_singleton && noex == NOEX_MODFUNC) {
 	rb_add_method(rb_singleton_class(klass), id, VM_METHOD_TYPE_ISEQ, miseq, NOEX_PUBLIC);
@@ -1873,10 +1927,10 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
 } while (0)
 
 static VALUE
-m_core_define_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
+m_core_define_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval, VALUE nested)
 {
     REWIND_CFP({
-	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, 0, rb_vm_cref());
+	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, nested, 0, rb_vm_cref());
     });
     return Qnil;
 }
@@ -1885,7 +1939,7 @@ static VALUE
 m_core_define_singleton_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
 {
     REWIND_CFP({
-	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, 1, rb_vm_cref());
+	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, Qfalse, 1, rb_vm_cref());
     });
     return Qnil;
 }
@@ -2001,7 +2055,7 @@ Init_VM(void)
     rb_define_method_id(klass, id_core_set_method_alias, m_core_set_method_alias, 3);
     rb_define_method_id(klass, id_core_set_variable_alias, m_core_set_variable_alias, 2);
     rb_define_method_id(klass, id_core_undef_method, m_core_undef_method, 2);
-    rb_define_method_id(klass, id_core_define_method, m_core_define_method, 3);
+    rb_define_method_id(klass, id_core_define_method, m_core_define_method, 4);
     rb_define_method_id(klass, id_core_define_singleton_method, m_core_define_singleton_method, 3);
     rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 1);
     rb_obj_freeze(fcore);
diff --git a/vm_core.h b/vm_core.h
index 811e7ff..5a5d08e 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -717,6 +717,8 @@ rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t flag, VALUE self,
     } \
 } while (0)
 
+void rb_overlay_module(NODE*, VALUE, VALUE);
+
 #if defined __GNUC__ && __GNUC__ >= 4
 #pragma GCC visibility push(default)
 #endif
diff --git a/vm_method.c b/vm_method.c
index 815fd43..2d362f7 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -711,9 +711,14 @@ rb_undef(VALUE klass, ID id)
     }
 
     if (!RTEST(rb_class_inherited_p(klass, me->klass))) {
-	VALUE mod = rb_module_new();
-	rb_overlay_module(cref, klass, mod);
-	klass = mod;
+	if (FL_TEST(me->klass, RMODULE_HAS_NESTED_METHODS)) {
+	    klass = me->klass;
+	}
+	else {
+	    VALUE mod = rb_module_new();
+	    rb_overlay_module(cref, klass, mod);
+	    klass = mod;
+	}
     }
     rb_add_method(klass, id, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC);
 
