Feature #11415 ยป autoload-proc.patch
| include/ruby/intern.h | ||
|---|---|---|
|
void rb_name_class(VALUE, ID);
|
||
|
VALUE rb_class_name(VALUE);
|
||
|
void rb_autoload(VALUE, ID, const char*);
|
||
|
void rb_autoload_value(VALUE, ID, VALUE);
|
||
|
VALUE rb_autoload_load(VALUE, ID);
|
||
|
VALUE rb_autoload_p(VALUE, ID);
|
||
|
VALUE rb_f_trace_var(int, const VALUE*);
|
||
| load.c | ||
|---|---|---|
|
{
|
||
|
ID id = rb_to_id(sym);
|
||
|
FilePathValue(file);
|
||
|
rb_autoload(mod, id, RSTRING_PTR(file));
|
||
|
if (rb_obj_is_proc(file)) {
|
||
|
rb_autoload_value(mod, id, file);
|
||
|
} else {
|
||
|
FilePathValue(file);
|
||
|
rb_autoload(mod, id, RSTRING_PTR(file));
|
||
|
}
|
||
|
return Qnil;
|
||
|
}
|
||
| test/ruby/test_autoload.rb | ||
|---|---|---|
|
Kernel.module_eval do; alias :require :old_require; undef :old_require; end
|
||
|
end
|
||
|
def test_simple_proc_invocation
|
||
|
calls = 0
|
||
|
add_autoload -> {
|
||
|
calls += 1
|
||
|
::Object.const_set(:AutoloadTest, 999)
|
||
|
0
|
||
|
}
|
||
|
assert_equal(999, Object::AutoloadTest)
|
||
|
assert_equal(999, Object::AutoloadTest)
|
||
|
assert_equal(1, calls)
|
||
|
ensure
|
||
|
remove_autoload_constant
|
||
|
end
|
||
|
def test_concurrent_proc_invocation
|
||
|
require 'monitor'
|
||
|
calls = 0
|
||
|
n = 0
|
||
|
m = Monitor.new
|
||
|
add_autoload -> {
|
||
|
calls += 1
|
||
|
sleep 0.1 until calls >= 2
|
||
|
m.synchronize {
|
||
|
if ::Object.autoload?(:AutoloadTest)
|
||
|
::Object.const_set :AutoloadTest, "x#{n += 1}"
|
||
|
end
|
||
|
}
|
||
|
}
|
||
|
t1 = Thread.new { Object::AutoloadTest }
|
||
|
t2 = Thread.new { Object::AutoloadTest }
|
||
|
assert_equal 'x1', t1.value
|
||
|
assert_equal 'x1', t2.value
|
||
|
assert_equal(2, calls)
|
||
|
assert_equal(1, n)
|
||
|
ensure
|
||
|
remove_autoload_constant
|
||
|
end
|
||
|
def test_proc_with_class_definition
|
||
|
add_autoload -> {
|
||
|
Tempfile.create(['autoload', '.rb']) {|file|
|
||
|
file.puts 'class AutoloadTest; end'
|
||
|
file.close
|
||
|
@autoload_paths << file.path
|
||
|
require file.path
|
||
|
}
|
||
|
}
|
||
|
assert(Object::AutoloadTest)
|
||
|
ensure
|
||
|
remove_autoload_constant
|
||
|
end
|
||
|
def add_autoload(path)
|
||
|
(@autoload_paths ||= []) << path
|
||
|
::Object.class_eval {autoload(:AutoloadTest, path)}
|
||
| variable.c | ||
|---|---|---|
|
void
|
||
|
rb_autoload(VALUE mod, ID id, const char *file)
|
||
|
{
|
||
|
VALUE fn;
|
||
|
if (!file || !*file) {
|
||
|
rb_raise(rb_eArgError, "empty file name");
|
||
|
}
|
||
|
fn = rb_str_new2(file);
|
||
|
FL_UNSET(fn, FL_TAINT);
|
||
|
OBJ_FREEZE(fn);
|
||
|
rb_autoload_value(mod, id, fn);
|
||
|
}
|
||
|
void
|
||
|
rb_autoload_value(VALUE mod, ID id, VALUE source)
|
||
|
{
|
||
|
st_data_t av;
|
||
|
VALUE ad, fn;
|
||
|
VALUE ad;
|
||
|
struct st_table *tbl;
|
||
|
struct autoload_data_i *ele;
|
||
|
rb_const_entry_t *ce;
|
||
| ... | ... | |
|
rb_raise(rb_eNameError, "autoload must be constant name: %"PRIsVALUE"",
|
||
|
QUOTE_ID(id));
|
||
|
}
|
||
|
if (!file || !*file) {
|
||
|
rb_raise(rb_eArgError, "empty file name");
|
||
|
}
|
||
|
ce = rb_const_lookup(mod, id);
|
||
|
if (ce && ce->value != Qundef) {
|
||
| ... | ... | |
|
RB_OBJ_WRITTEN(mod, Qnil, av);
|
||
|
DATA_PTR(av) = tbl = st_init_numtable();
|
||
|
}
|
||
|
fn = rb_str_new2(file);
|
||
|
FL_UNSET(fn, FL_TAINT);
|
||
|
OBJ_FREEZE(fn);
|
||
|
ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele);
|
||
|
ele->feature = fn;
|
||
|
ele->feature = source;
|
||
|
ele->safe_level = rb_safe_level();
|
||
|
ele->thread = Qnil;
|
||
|
ele->value = Qundef;
|
||
| ... | ... | |
|
return 0;
|
||
|
}
|
||
|
file = ele->feature;
|
||
|
if (rb_obj_is_proc(file)) {
|
||
|
if (ele->thread == rb_thread_current()) {
|
||
|
return 0;
|
||
|
} else {
|
||
|
return load;
|
||
|
}
|
||
|
}
|
||
|
Check_Type(file, T_STRING);
|
||
|
if (!RSTRING_PTR(file) || !*RSTRING_PTR(file)) {
|
||
|
rb_raise(rb_eArgError, "empty file name");
|
||
| ... | ... | |
|
autoload_require(VALUE arg)
|
||
|
{
|
||
|
struct autoload_data_i *ele = (struct autoload_data_i *)arg;
|
||
|
return rb_funcall(rb_vm_top_self(), rb_intern("require"), 1, ele->feature);
|
||
|
if (rb_obj_is_proc(ele->feature)) {
|
||
|
return rb_funcall(ele->feature, rb_intern("call"), 0);
|
||
|
} else {
|
||
|
return rb_funcall(rb_vm_top_self(), rb_intern("require"), 1, ele->feature);
|
||
|
}
|
||
|
}
|
||
|
static VALUE
|
||