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
|