* eval.c (rb_autoload_method, rb_autoload_method_p): method
  autoload.  (EXPERIMENTAL)  [ruby-talk:152194]

Index: eval.c
===================================================================
RCS file: /cvs/ruby/src/ruby/eval.c,v
retrieving revision 1.817
diff -U2 -p -r1.817 eval.c
--- eval.c	16 Aug 2005 03:43:44 -0000	1.817
+++ eval.c	16 Aug 2005 03:47:07 -0000
@@ -378,5 +378,5 @@ static ID added, singleton_added;
 static ID __id__, __send__, respond_to;
 
-void
+NODE *
 rb_add_method(klass, mid, node, noex)
     VALUE klass;
@@ -414,4 +414,5 @@ rb_add_method(klass, mid, node, noex)
 	}
     }
+    return body;
 }
 
@@ -5985,4 +5986,29 @@ rb_call0(klass, recv, id, oid, argc, arg
 }
 
+#define NODE_LOAD NODE_MEMO
+
+static void
+autoload_method(klass, mid, body)
+    VALUE klass;
+    ID mid;
+    NODE *body;
+{
+    VALUE path;
+    int safe;
+    st_data_t n;
+    NODE *meth;
+
+    if (!st_delete(RCLASS(klass)->m_tbl, &mid, &n) ||
+	!(meth = (NODE *)n) || meth->nd_body != body) {
+	rb_name_error(mid, "method `%s' not defined in %s",
+		      rb_id2name(mid), rb_class2name(klass));
+    }
+    path = rb_str_new2(body->nd_file);
+    OBJ_FREEZE(path);
+    safe = nd_line(body);
+    rb_clear_cache_for_undef(klass, mid);
+    rb_require_safe(path, safe);
+}
+
 static VALUE
 rb_call(klass, recv, mid, argc, argv, scope)
@@ -5995,5 +6021,5 @@ rb_call(klass, recv, mid, argc, argv, sc
     NODE  *body;		/* OK */
     int    noex;
-    ID     id = mid;
+    ID     id;
     struct cache_entry *ent;
 
@@ -6002,4 +6028,6 @@ rb_call(klass, recv, mid, argc, argv, sc
 		 rb_id2name(mid), recv);
     }
+  again:
+    id = mid;
     /* is it in the method cache? */
     ent = cache + EXPR1(klass, mid);
@@ -6036,4 +6064,9 @@ rb_call(klass, recv, mid, argc, argv, sc
     }
 
+    if (nd_type(body) == NODE_LOAD) {
+	autoload_method(klass, id, body);
+	goto again;
+    }
+
     return rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER);
 }
@@ -8029,4 +8062,78 @@ rb_f_autoload_p(obj, sym)
 
 void
+rb_autoload_method(module, name, path)
+    VALUE module;
+    ID name;
+    const char *path;
+{
+    NODE *meth = rb_node_newnode(NODE_LOAD, 0, 0, 0);
+
+    meth->nd_file = rb_source_filename(path);
+    nd_set_line(meth, ruby_safe_level);
+    meth = rb_add_method(module, name, meth, NOEX_PUBLIC);
+    meth->nd_cnt++;		/* allow to redefine gently */
+}
+
+const char *
+rb_autoload_method_p(module, name)
+    VALUE module;
+    ID name;
+{
+    int noex;
+    NODE *meth = rb_get_method_body(&module, &name, &noex);
+
+    if (!meth || nd_type(meth) != NODE_LOAD) return NULL;
+    return meth->nd_file;
+}
+
+/*
+ *  call-seq:
+ *     mod.autoload_method(name, filename)   => nil
+ *
+ *  Registers _filename_ to be loaded (using <code>Kernel::require</code>)
+ *  the first time that _method_ (which may be a <code>String</code> or
+ *  a symbol) of _mod_ is called.
+ *
+ *     class << Time
+ *       autoload_method(:parse, "time")
+ *     end
+ *     Time.parse("2005-08-15")     # autoloads "time"
+ */
+
+static VALUE
+rb_mod_autoload_method(module, name, path)
+    VALUE module, name, path;
+{
+    FilePathValue(path);
+    rb_autoload_method(module, rb_to_id(name), RSTRING(path)->ptr);
+    return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     mod.autoload?(name)   => String or nil
+ *  
+ *  Returns _filename_ to be loaded if _method_ of _mod_ is registered
+ *  as +autoload+.
+ *
+ *     class << Time
+ *       autoload_method(:parse, "time")
+ *     end
+ *     class << Time
+ *       autoload_method?(:parse)     # => "time"
+ *     end
+ */
+
+static VALUE
+rb_mod_autoload_method_p(module, name)
+    VALUE module, name;
+{
+    const char *s = rb_autoload_method_p(module, rb_to_id(name));
+
+    if (!s) return Qnil;
+    return rb_str_new2(s);
+}
+
+void
 Init_load()
 {
@@ -8044,4 +8151,6 @@ Init_load()
     rb_define_method(rb_cModule, "autoload",  rb_mod_autoload,   2);
     rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
+    rb_define_method(rb_cModule, "autoload_method", rb_mod_autoload_method, 2);
+    rb_define_method(rb_cModule, "autoload_method?", rb_mod_autoload_method_p, 1);
     rb_define_global_function("autoload",  rb_f_autoload,   2);
     rb_define_global_function("autoload?", rb_f_autoload_p, 1);
@@ -9190,4 +9299,5 @@ rb_method_call(argc, argv, method)
     VALUE result = Qnil;	/* OK */
     struct METHOD *data;
+    NODE *body;
     int state;
     volatile int safe = -1;
@@ -9197,4 +9307,16 @@ rb_method_call(argc, argv, method)
 	rb_raise(rb_eTypeError, "can't call unbound method; bind first");
     }
+    body = data->body;
+    while (nd_type(body) == NODE_LOAD) {
+	VALUE newmethod;
+	struct METHOD *data2;
+
+	autoload_method(data->klass, data->id, body);
+	newmethod = mnew(data->rklass, data->recv, data->oid, rb_obj_class(method));
+	Data_Get_Struct(newmethod, struct METHOD, data2);
+	data->klass = data2->klass;
+	data->id = data2->id;
+	data->body = body = data2->body;
+    }
     PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
     PUSH_TAG(PROT_NONE);
Index: gc.c
===================================================================
RCS file: /cvs/ruby/src/ruby/gc.c,v
retrieving revision 1.206
diff -U2 -p -r1.206 gc.c
--- gc.c	12 Aug 2005 08:13:28 -0000	1.206
+++ gc.c	16 Aug 2005 02:55:31 -0000
@@ -1512,4 +1512,7 @@ Init_heap()
     }
     add_heap();
+    if (!source_filenames) {
+	source_filenames = st_init_strtable();
+    }
 }
 
@@ -1937,6 +1940,4 @@ Init_GC()
     finalizers = rb_ary_new();
 
-    source_filenames = st_init_strtable();
-
     nomem_error = rb_exc_new2(rb_eNoMemError, "failed to allocate memory");
     rb_global_variable(&nomem_error);
Index: intern.h
===================================================================
RCS file: /cvs/ruby/src/ruby/intern.h,v
retrieving revision 1.177
diff -U2 -p -r1.177 intern.h
--- intern.h	14 Aug 2005 22:25:09 -0000	1.177
+++ intern.h	16 Aug 2005 02:35:57 -0000
@@ -247,4 +247,6 @@ void rb_set_end_proc _((void (*)(VALUE),
 void rb_mark_end_proc _((void));
 void rb_exec_end_proc _((void));
+void rb_autoload_method _((VALUE, ID, const char *));
+const char *rb_autoload_method_p _((VALUE, ID));
 void ruby_finalize _((void));
 NORETURN(void ruby_stop _((int)));
Index: node.h
===================================================================
RCS file: /cvs/ruby/src/ruby/node.h,v
retrieving revision 1.68
diff -U2 -p -r1.68 node.h
--- node.h	27 Jul 2005 07:27:18 -0000	1.68
+++ node.h	29 Jul 2005 01:01:33 -0000
@@ -363,5 +363,5 @@ NODE *rb_compile_string _((const char*, 
 NODE *rb_compile_file _((const char*, VALUE, int));
 
-void rb_add_method _((VALUE, ID, NODE *, int));
+NODE *rb_add_method _((VALUE, ID, NODE *, int));
 NODE *rb_node_newnode _((enum node_type,VALUE,VALUE,VALUE));
 
Index: time.c
===================================================================
RCS file: /cvs/ruby/src/ruby/time.c,v
retrieving revision 1.103
diff -U2 -p -r1.103 time.c
--- time.c	4 Mar 2005 06:47:41 -0000	1.103
+++ time.c	16 Aug 2005 02:44:16 -0000
@@ -2118,3 +2118,20 @@ Init_Time()
     rb_define_method(rb_cTime, "marshal_load", time_mload, 1);
 #endif
+
+    {
+	VALUE c = rb_cTime;
+#define AUTOLOAD(meth) rb_autoload_method(c, rb_intern(meth), "time")
+
+	AUTOLOAD("iso8601");
+	AUTOLOAD("xmlschema");
+	AUTOLOAD("httpdate");
+
+	c = CLASS_OF(c);
+	AUTOLOAD("parse");
+	AUTOLOAD("iso8601");
+	AUTOLOAD("zone_offset");
+	AUTOLOAD("xmlschema");
+	AUTOLOAD("httpdate");
+	AUTOLOAD("strptime");
+    }
 }
