Feature #11415 ยป autoload-proc.patch
include/ruby/intern.h | ||
---|---|---|
935 | 935 |
void rb_name_class(VALUE, ID); |
936 | 936 |
VALUE rb_class_name(VALUE); |
937 | 937 |
void rb_autoload(VALUE, ID, const char*); |
938 |
void rb_autoload_value(VALUE, ID, VALUE); |
|
938 | 939 |
VALUE rb_autoload_load(VALUE, ID); |
939 | 940 |
VALUE rb_autoload_p(VALUE, ID); |
940 | 941 |
VALUE rb_f_trace_var(int, const VALUE*); |
load.c | ||
---|---|---|
1123 | 1123 |
{ |
1124 | 1124 |
ID id = rb_to_id(sym); |
1125 | 1125 | |
1126 |
FilePathValue(file); |
|
1127 |
rb_autoload(mod, id, RSTRING_PTR(file)); |
|
1126 |
if (rb_obj_is_proc(file)) { |
|
1127 |
rb_autoload_value(mod, id, file); |
|
1128 |
} else { |
|
1129 |
FilePathValue(file); |
|
1130 |
rb_autoload(mod, id, RSTRING_PTR(file)); |
|
1131 |
} |
|
1128 | 1132 |
return Qnil; |
1129 | 1133 |
} |
1130 | 1134 |
test/ruby/test_autoload.rb | ||
---|---|---|
212 | 212 |
Kernel.module_eval do; alias :require :old_require; undef :old_require; end |
213 | 213 |
end |
214 | 214 | |
215 |
def test_simple_proc_invocation |
|
216 |
calls = 0 |
|
217 |
add_autoload -> { |
|
218 |
calls += 1 |
|
219 |
::Object.const_set(:AutoloadTest, 999) |
|
220 |
0 |
|
221 |
} |
|
222 |
assert_equal(999, Object::AutoloadTest) |
|
223 |
assert_equal(999, Object::AutoloadTest) |
|
224 |
assert_equal(1, calls) |
|
225 |
ensure |
|
226 |
remove_autoload_constant |
|
227 |
end |
|
228 | ||
229 |
def test_concurrent_proc_invocation |
|
230 |
require 'monitor' |
|
231 |
calls = 0 |
|
232 |
n = 0 |
|
233 |
m = Monitor.new |
|
234 |
add_autoload -> { |
|
235 |
calls += 1 |
|
236 |
sleep 0.1 until calls >= 2 |
|
237 |
m.synchronize { |
|
238 |
if ::Object.autoload?(:AutoloadTest) |
|
239 |
::Object.const_set :AutoloadTest, "x#{n += 1}" |
|
240 |
end |
|
241 |
} |
|
242 |
} |
|
243 |
t1 = Thread.new { Object::AutoloadTest } |
|
244 |
t2 = Thread.new { Object::AutoloadTest } |
|
245 |
assert_equal 'x1', t1.value |
|
246 |
assert_equal 'x1', t2.value |
|
247 |
assert_equal(2, calls) |
|
248 |
assert_equal(1, n) |
|
249 |
ensure |
|
250 |
remove_autoload_constant |
|
251 |
end |
|
252 | ||
253 |
def test_proc_with_class_definition |
|
254 |
add_autoload -> { |
|
255 |
Tempfile.create(['autoload', '.rb']) {|file| |
|
256 |
file.puts 'class AutoloadTest; end' |
|
257 |
file.close |
|
258 |
@autoload_paths << file.path |
|
259 |
require file.path |
|
260 |
} |
|
261 |
} |
|
262 |
assert(Object::AutoloadTest) |
|
263 |
ensure |
|
264 |
remove_autoload_constant |
|
265 |
end |
|
266 | ||
215 | 267 |
def add_autoload(path) |
216 | 268 |
(@autoload_paths ||= []) << path |
217 | 269 |
::Object.class_eval {autoload(:AutoloadTest, path)} |
variable.c | ||
---|---|---|
1909 | 1909 |
void |
1910 | 1910 |
rb_autoload(VALUE mod, ID id, const char *file) |
1911 | 1911 |
{ |
1912 |
VALUE fn; |
|
1913 | ||
1914 |
if (!file || !*file) { |
|
1915 |
rb_raise(rb_eArgError, "empty file name"); |
|
1916 |
} |
|
1917 |
fn = rb_str_new2(file); |
|
1918 |
FL_UNSET(fn, FL_TAINT); |
|
1919 |
OBJ_FREEZE(fn); |
|
1920 | ||
1921 |
rb_autoload_value(mod, id, fn); |
|
1922 |
} |
|
1923 | ||
1924 |
void |
|
1925 |
rb_autoload_value(VALUE mod, ID id, VALUE source) |
|
1926 |
{ |
|
1912 | 1927 |
st_data_t av; |
1913 |
VALUE ad, fn;
|
|
1928 |
VALUE ad; |
|
1914 | 1929 |
struct st_table *tbl; |
1915 | 1930 |
struct autoload_data_i *ele; |
1916 | 1931 |
rb_const_entry_t *ce; |
... | ... | |
1919 | 1934 |
rb_raise(rb_eNameError, "autoload must be constant name: %"PRIsVALUE"", |
1920 | 1935 |
QUOTE_ID(id)); |
1921 | 1936 |
} |
1922 |
if (!file || !*file) { |
|
1923 |
rb_raise(rb_eArgError, "empty file name"); |
|
1924 |
} |
|
1925 | 1937 | |
1926 | 1938 |
ce = rb_const_lookup(mod, id); |
1927 | 1939 |
if (ce && ce->value != Qundef) { |
... | ... | |
1940 | 1952 |
RB_OBJ_WRITTEN(mod, Qnil, av); |
1941 | 1953 |
DATA_PTR(av) = tbl = st_init_numtable(); |
1942 | 1954 |
} |
1943 |
fn = rb_str_new2(file); |
|
1944 |
FL_UNSET(fn, FL_TAINT); |
|
1945 |
OBJ_FREEZE(fn); |
|
1946 | 1955 | |
1947 | 1956 |
ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele); |
1948 |
ele->feature = fn;
|
|
1957 |
ele->feature = source;
|
|
1949 | 1958 |
ele->safe_level = rb_safe_level(); |
1950 | 1959 |
ele->thread = Qnil; |
1951 | 1960 |
ele->value = Qundef; |
... | ... | |
1995 | 2004 |
return 0; |
1996 | 2005 |
} |
1997 | 2006 |
file = ele->feature; |
2007 |
if (rb_obj_is_proc(file)) { |
|
2008 |
if (ele->thread == rb_thread_current()) { |
|
2009 |
return 0; |
|
2010 |
} else { |
|
2011 |
return load; |
|
2012 |
} |
|
2013 |
} |
|
1998 | 2014 |
Check_Type(file, T_STRING); |
1999 | 2015 |
if (!RSTRING_PTR(file) || !*RSTRING_PTR(file)) { |
2000 | 2016 |
rb_raise(rb_eArgError, "empty file name"); |
... | ... | |
2064 | 2080 |
autoload_require(VALUE arg) |
2065 | 2081 |
{ |
2066 | 2082 |
struct autoload_data_i *ele = (struct autoload_data_i *)arg; |
2067 |
return rb_funcall(rb_vm_top_self(), rb_intern("require"), 1, ele->feature); |
|
2083 |
if (rb_obj_is_proc(ele->feature)) { |
|
2084 |
return rb_funcall(ele->feature, rb_intern("call"), 0); |
|
2085 |
} else { |
|
2086 |
return rb_funcall(rb_vm_top_self(), rb_intern("require"), 1, ele->feature); |
|
2087 |
} |
|
2068 | 2088 |
} |
2069 | 2089 | |
2070 | 2090 |
static VALUE |
2071 |
- |