Project

General

Profile

Feature #11415 ยป autoload-proc.patch

matthewd (Matthew Draper), 08/05/2015 02:23 AM

View differences:

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
    (1-1/1)