Project

General

Profile

Feature #11415 ยป autoload-proc.patch

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

View differences:

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
-