require_performance.diff

Xavier Shay, 05/23/2011 07:56 PM

Download (28.6 KB)

View differences:

.gitignore
129 129

  
130 130
# /win32/
131 131
/win32/*.ico
132
ext/win32ole/.document
array.c
305 305
    return (VALUE)ary;
306 306
}
307 307

  
308
static VALUE
308
VALUE // TODO: Removed the static declaration off this, figure out if that is OK
309 309
ary_new(VALUE klass, long capa)
310 310
{
311 311
    VALUE ary;
enumerator.c
1171 1171
    id_rewind = rb_intern("rewind");
1172 1172
    id_each = rb_intern("each");
1173 1173
    sym_each = ID2SYM(id_each);
1174

  
1175
    rb_provide("enumerator.so");	/* for backward compatibility */
1176 1174
}
file.c
1270 1270
 * Return <code>true</code> if the named file exists.
1271 1271
 */
1272 1272

  
1273
static VALUE
1273
VALUE // TODO: Mark this as static again
1274 1274
rb_file_exist_p(VALUE obj, VALUE fname)
1275 1275
{
1276 1276
    struct stat st;
lib/enumerator.rb
1
# This class is now defined entirely in enumerator.c and is always available.
2
# This file needs to remain here for backwards compatibility, so that `require
3
# "enumerator"` will not raise an exception.
load.c
18 18
#endif
19 19

  
20 20

  
21
VALUE rb_f_require(VALUE, VALUE);
22
VALUE rb_f_require_relative(VALUE, VALUE);
23
static VALUE rb_f_load(int, VALUE *);
24
VALUE rb_require_safe(VALUE, int);
25

  
26
static int rb_file_has_been_required(VALUE);
27
static int rb_file_is_being_required(VALUE);
28
static int rb_file_is_ruby(VALUE);
29
static st_table * get_loaded_features_hash(void);
30
static void rb_load_internal(VALUE, int);
31
static char * load_lock(const char *);
32
static void load_unlock(const char *, int);
33
static void load_failed(VALUE fname);
34

  
35
void rb_provide(const char *feature);
36
static void rb_provide_feature(VALUE);
37

  
38
/* 
39
 * TODO: These functions are all conceptually related, and should be extracted
40
 * into a separate file.
41
 */
42
static VALUE rb_locate_file(VALUE);
43
static VALUE rb_locate_file_absolute(VALUE);
44
static VALUE rb_locate_file_relative(VALUE);
45
static VALUE rb_locate_file_in_load_path(VALUE);
46
static VALUE rb_locate_file_with_extensions(VALUE);
47
static int rb_path_is_absolute(VALUE);
48
static int rb_path_is_relative(VALUE);
49
VALUE rb_get_expanded_load_path();
50

  
51
/* 
52
 * TODO: These functions are all conceptually related, and should be extracted
53
 * into a separate file.
54
 */
55
static VALUE rb_cLoadedFeaturesProxy;
56
static void rb_rehash_loaded_features();
57
static VALUE rb_loaded_features_hook(int, VALUE*, VALUE);
58
static void define_loaded_features_proxy();
59

  
60
VALUE ary_new(VALUE, long); // array.c
61

  
21 62
static const char *const loadable_ext[] = {
22
    ".rb", DLEXT,
63
    ".rb", 
64
    DLEXT,
23 65
#ifdef DLEXT2
24 66
    DLEXT2,
25 67
#endif
......
33 75
    return load_path;
34 76
}
35 77

  
78
static st_table *
79
get_loaded_features_hash(void)
80
{
81
    st_table* loaded_features_hash;
82
    loaded_features_hash = GET_VM()->loaded_features_hash;
83

  
84
    if (!loaded_features_hash) {
85
      GET_VM()->loaded_features_hash = loaded_features_hash = st_init_strcasetable();
86
    }
87

  
88
    return loaded_features_hash;
89
}
90

  
91
static st_table *
92
get_filename_expansion_hash(void)
93
{
94
    st_table* filename_expansion_hash;
95
    filename_expansion_hash = GET_VM()->filename_expansion_hash;
96

  
97
    if (!filename_expansion_hash) {
98
      GET_VM()->filename_expansion_hash = filename_expansion_hash = st_init_strcasetable();
99
    }
100

  
101
    return filename_expansion_hash;
102
}
103

  
36 104
VALUE
37 105
rb_get_expanded_load_path(void)
38 106
{
......
68 136
    return GET_VM()->loading_table;
69 137
}
70 138

  
71
static VALUE
72
loaded_feature_path(const char *name, long vlen, const char *feature, long len,
73
		    int type, VALUE load_path)
74
{
75
    long i;
76

  
77
    for (i = 0; i < RARRAY_LEN(load_path); ++i) {
78
	VALUE p = RARRAY_PTR(load_path)[i];
79
	const char *s = StringValuePtr(p);
80
	long n = RSTRING_LEN(p);
81

  
82
	if (vlen < n + len + 1) continue;
83
	if (n && (strncmp(name, s, n) || name[n] != '/')) continue;
84
	if (strncmp(name + n + 1, feature, len)) continue;
85
	if (name[n+len+1] && name[n+len+1] != '.') continue;
86
	switch (type) {
87
	  case 's':
88
	    if (IS_DLEXT(&name[n+len+1])) return p;
89
	    break;
90
	  case 'r':
91
	    if (IS_RBEXT(&name[n+len+1])) return p;
92
	    break;
93
	  default:
94
	    return p;
95
	}
96
    }
97
    return 0;
98
}
99

  
100
struct loaded_feature_searching {
101
    const char *name;
102
    long len;
103
    int type;
104
    VALUE load_path;
105
    const char *result;
106
};
107

  
108
static int
109
loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
110
{
111
    const char *s = (const char *)v;
112
    struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f;
113
    VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len,
114
				  fp->type, fp->load_path);
115
    if (!p) return ST_CONTINUE;
116
    fp->result = s;
117
    return ST_STOP;
118
}
119

  
120
static int
121
rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn)
122
{
123
    VALUE v, features, p, load_path = 0;
124
    const char *f, *e;
125
    long i, len, elen, n;
126
    st_table *loading_tbl;
127
    st_data_t data;
128
    int type;
129

  
130
    if (fn) *fn = 0;
131
    if (ext) {
132
	elen = strlen(ext);
133
	len = strlen(feature) - elen;
134
	type = rb ? 'r' : 's';
135
    }
136
    else {
137
	len = strlen(feature);
138
	elen = 0;
139
	type = 0;
140
    }
141
    features = get_loaded_features();
142
    for (i = 0; i < RARRAY_LEN(features); ++i) {
143
	v = RARRAY_PTR(features)[i];
144
	f = StringValuePtr(v);
145
	if ((n = RSTRING_LEN(v)) < len) continue;
146
	if (strncmp(f, feature, len) != 0) {
147
	    if (expanded) continue;
148
	    if (!load_path) load_path = rb_get_expanded_load_path();
149
	    if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
150
		continue;
151
	    expanded = 1;
152
	    f += RSTRING_LEN(p) + 1;
153
	}
154
	if (!*(e = f + len)) {
155
	    if (ext) continue;
156
	    return 'u';
157
	}
158
	if (*e != '.') continue;
159
	if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
160
	    return 's';
161
	}
162
	if ((rb || !ext) && (IS_RBEXT(e))) {
163
	    return 'r';
164
	}
165
    }
166
    loading_tbl = get_loading_table();
167
    if (loading_tbl) {
168
	f = 0;
169
	if (!expanded) {
170
	    struct loaded_feature_searching fs;
171
	    fs.name = feature;
172
	    fs.len = len;
173
	    fs.type = type;
174
	    fs.load_path = load_path ? load_path : rb_get_load_path();
175
	    fs.result = 0;
176
	    st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
177
	    if ((f = fs.result) != 0) {
178
		if (fn) *fn = f;
179
		goto loading;
180
	    }
181
	}
182
	if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
183
	    if (fn) *fn = (const char*)data;
184
	  loading:
185
	    if (!ext) return 'u';
186
	    return !IS_RBEXT(ext) ? 's' : 'r';
187
	}
188
	else {
189
	    VALUE bufstr;
190
	    char *buf;
191

  
192
	    if (ext && *ext) return 0;
193
	    bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
194
	    buf = RSTRING_PTR(bufstr);
195
	    MEMCPY(buf, feature, char, len);
196
	    for (i = 0; (e = loadable_ext[i]) != 0; i++) {
197
		strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
198
		if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
199
		    rb_str_resize(bufstr, 0);
200
		    if (fn) *fn = (const char*)data;
201
		    return i ? 's' : 'r';
202
		}
203
	    }
204
	    rb_str_resize(bufstr, 0);
205
	}
206
    }
207
    return 0;
208
}
209

  
210 139
int
211 140
rb_provided(const char *feature)
212 141
{
213 142
    return rb_feature_provided(feature, 0);
214 143
}
215 144

  
216
int
217
rb_feature_provided(const char *feature, const char **loading)
145

  
146
/* Mark the given feature as loaded, after it has been evaluated. */
147
	static void
148
rb_provide_feature(VALUE feature)
218 149
{
219
    const char *ext = strrchr(feature, '.');
220
    volatile VALUE fullpath = 0;
150
	int frozen = 0;
151
	st_table* loaded_features_hash;
221 152

  
222
    if (*feature == '.' &&
223
	(feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
224
	fullpath = rb_file_expand_path(rb_str_new2(feature), Qnil);
225
	feature = RSTRING_PTR(fullpath);
226
    }
227
    if (ext && !strchr(ext, '/')) {
228
	if (IS_RBEXT(ext)) {
229
	    if (rb_feature_p(feature, ext, TRUE, FALSE, loading)) return TRUE;
230
	    return FALSE;
153
	if (OBJ_FROZEN(get_loaded_features())) {
154
		rb_raise(rb_eRuntimeError,
155
				"$LOADED_FEATURES is frozen; cannot append feature");
231 156
	}
232
	else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
233
	    if (rb_feature_p(feature, ext, FALSE, FALSE, loading)) return TRUE;
234
	    return FALSE;
235
	}
236
    }
237
    if (rb_feature_p(feature, 0, TRUE, FALSE, loading))
238
	return TRUE;
239
    return FALSE;
240
}
241 157

  
242
static void
243
rb_provide_feature(VALUE feature)
244
{
245
    if (OBJ_FROZEN(get_loaded_features())) {
246
	rb_raise(rb_eRuntimeError,
247
		 "$LOADED_FEATURES is frozen; cannot append feature");
248
    }
249
    rb_ary_push(get_loaded_features(), feature);
158
	loaded_features_hash = get_loaded_features_hash();
159
	st_insert(
160
			loaded_features_hash,
161
			(st_data_t)ruby_strdup(RSTRING_PTR(feature)),
162
			(st_data_t)rb_barrier_new());
163

  
164
	rb_ary_push(get_loaded_features(), feature);
250 165
}
251 166

  
252 167
void
......
418 333

  
419 334

  
420 335
/*
421
 *  call-seq:
422
 *     require(string)    -> true or false
423
 *
424
 *  Ruby tries to load the library named _string_, returning
425
 *  +true+ if successful. If the filename does not resolve to
426
 *  an absolute path, it will be searched for in the directories listed
427
 *  in <code>$:</code>. If the file has the extension ``.rb'', it is
428
 *  loaded as a source file; if the extension is ``.so'', ``.o'', or
429
 *  ``.dll'', or whatever the default shared library extension is on
430
 *  the current platform, Ruby loads the shared library as a Ruby
431
 *  extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on
432
 *  to the name. The name of the loaded feature is added to the array in
433
 *  <code>$"</code>. A feature will not be loaded if its name already
434
 *  appears in <code>$"</code>. The file name is converted to an absolute
435
 *  path, so ``<code>require 'a'; require './a'</code>'' will not load
436
 *  <code>a.rb</code> twice.
437
 *
438
 *     require "my-library.rb"
439
 *     require "db-driver"
440
 */
441

  
442
VALUE
443
rb_f_require(VALUE obj, VALUE fname)
444
{
445
    return rb_require_safe(fname, rb_safe_level());
446
}
447

  
448
/*
449 336
 * call-seq:
450 337
 *   require_relative(string) -> true or false
451 338
 *
......
465 352
    return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level());
466 353
}
467 354

  
468
static int
469
search_required(VALUE fname, volatile VALUE *path, int safe_level)
470
{
471
    VALUE tmp;
472
    char *ext, *ftptr;
473
    int type, ft = 0;
474
    const char *loading;
475

  
476
    *path = 0;
477
    ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
478
    if (ext && !strchr(ext, '/')) {
479
	if (IS_RBEXT(ext)) {
480
	    if (rb_feature_p(ftptr, ext, TRUE, FALSE, &loading)) {
481
		if (loading) *path = rb_str_new2(loading);
482
		return 'r';
483
	    }
484
	    if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
485
		ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
486
		if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading)
487
		    *path = tmp;
488
		return 'r';
489
	    }
490
	    return 0;
491
	}
492
	else if (IS_SOEXT(ext)) {
493
	    if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
494
		if (loading) *path = rb_str_new2(loading);
495
		return 's';
496
	    }
497
	    tmp = rb_str_new(RSTRING_PTR(fname), ext - RSTRING_PTR(fname));
498
#ifdef DLEXT2
499
	    OBJ_FREEZE(tmp);
500
	    if (rb_find_file_ext_safe(&tmp, loadable_ext + 1, safe_level)) {
501
		ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
502
		if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
503
		    *path = tmp;
504
		return 's';
505
	    }
506
#else
507
	    rb_str_cat2(tmp, DLEXT);
508
	    OBJ_FREEZE(tmp);
509
	    if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) {
510
		ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
511
		if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
512
		    *path = tmp;
513
		return 's';
514
	    }
515
#endif
516
	}
517
	else if (IS_DLEXT(ext)) {
518
	    if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
519
		if (loading) *path = rb_str_new2(loading);
520
		return 's';
521
	    }
522
	    if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
523
		ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
524
		if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
525
		    *path = tmp;
526
		return 's';
527
	    }
528
	}
529
    }
530
    else if ((ft = rb_feature_p(ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
531
	if (loading) *path = rb_str_new2(loading);
532
	return 'r';
533
    }
534
    tmp = fname;
535
    type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level);
536
    switch (type) {
537
      case 0:
538
	if (ft)
539
	    break;
540
	ftptr = RSTRING_PTR(tmp);
541
	return rb_feature_p(ftptr, 0, FALSE, TRUE, 0);
542

  
543
      default:
544
	if (ft)
545
	    break;
546
      case 1:
547
	ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
548
	if (rb_feature_p(ftptr, ext, !--type, TRUE, &loading) && !loading)
549
	    break;
550
	*path = tmp;
551
    }
552
    return type ? 's' : 'r';
553
}
554 355

  
555 356
static void
556 357
load_failed(VALUE fname)
......
568 369
}
569 370

  
570 371
VALUE
571
rb_require_safe(VALUE fname, int safe)
572
{
573
    volatile VALUE result = Qnil;
574
    rb_thread_t *th = GET_THREAD();
575
    volatile VALUE errinfo = th->errinfo;
576
    int state;
577
    struct {
578
	int safe;
579
    } volatile saved;
580
    char *volatile ftptr = 0;
581

  
582
    PUSH_TAG();
583
    saved.safe = rb_safe_level();
584
    if ((state = EXEC_TAG()) == 0) {
585
	VALUE path;
586
	long handle;
587
	int found;
588

  
589
	rb_set_safe_level_force(safe);
590
	FilePathValue(fname);
591
	rb_set_safe_level_force(0);
592
	found = search_required(fname, &path, safe);
593
	if (found) {
594
	    if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
595
		result = Qfalse;
596
	    }
597
	    else {
598
		switch (found) {
599
		  case 'r':
600
		    rb_load_internal(path, 0);
601
		    break;
602

  
603
		  case 's':
604
		    handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
605
						    path, 0, path);
606
		    rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
607
		    break;
608
		}
609
		rb_provide_feature(path);
610
		result = Qtrue;
611
	    }
612
	}
613
    }
614
    POP_TAG();
615
    load_unlock(ftptr, !state);
616

  
617
    rb_set_safe_level_force(saved.safe);
618
    if (state) {
619
	JUMP_TAG(state);
620
    }
621

  
622
    if (NIL_P(result)) {
623
	load_failed(fname);
624
    }
625

  
626
    th->errinfo = errinfo;
627

  
628
    return result;
629
}
630

  
631
VALUE
632 372
rb_require(const char *fname)
633 373
{
634 374
    VALUE fn = rb_str_new2(fname);
......
741 481
    return rb_mod_autoload_p(klass, sym);
742 482
}
743 483

  
484
VALUE
485
rb_file_exist_p(VALUE obj, VALUE path);
486

  
487
static int
488
rb_feature_exists(VALUE expanded_path)
489
{
490
  return rb_funcall(rb_cFile, rb_intern("file?"), 1, expanded_path) == Qtrue;
491
}
492

  
493
const char *available_extensions[] = {
494
  ".rb",
495
  DLEXT,
496
#ifdef DLEXT2
497
  DLEXT2,
498
#endif
499
  ""
500
};
501

  
502
#ifdef DLEXT2
503
VALUE available_ext_rb_str[4];
504
#else
505
VALUE available_ext_rb_str[3];
506
#endif
507

  
508
const char *alternate_dl_extensions[] = {
509
  DLEXT,
510
#ifdef DLEXT2
511
  DLEXT2
512
#endif
513
};
514

  
515
#define CHAR_ARRAY_LEN(array) sizeof(array) / sizeof(char*)
516
#define VALUE_ARRAY_LEN(array) sizeof(array) / sizeof(VALUE)
517

  
518
static VALUE
519
rb_locate_file_with_extensions(VALUE base_file_name) {
520
	unsigned int j;
521
	VALUE file_name_with_extension;
522
	VALUE extension;
523
	VALUE directory, basename;
524

  
525
	extension = rb_funcall(rb_cFile, rb_intern("extname"), 1, base_file_name);
526

  
527
	if (RSTRING_LEN(extension) == 0) {
528
		for (j = 0; j < VALUE_ARRAY_LEN(available_ext_rb_str); ++j) {
529
			file_name_with_extension = rb_str_plus(
530
					base_file_name,
531
					available_ext_rb_str[j]);
532

  
533
			if (rb_feature_exists(file_name_with_extension)) {
534
				return file_name_with_extension;
535
			}
536
		}
537
	} else {
538
		if (rb_feature_exists(base_file_name)) {
539
			return base_file_name;
540
		} else {
541
			for (j = 0; j < CHAR_ARRAY_LEN(alternate_dl_extensions); ++j) {
542
				// Try loading the native DLEXT version of this platform.
543
				// This allows 'pathname.so' to require 'pathname.bundle' on OSX
544
				directory = rb_file_dirname(base_file_name);
545
				basename  = rb_funcall(rb_cFile, rb_intern("basename"), 2, 
546
						base_file_name, extension);
547
				basename  = rb_str_cat2(basename, alternate_dl_extensions[j]);
548

  
549
				file_name_with_extension = rb_funcall(rb_cFile, rb_intern("join"), 2, 
550
						directory, basename);
551

  
552
				if (rb_feature_exists(file_name_with_extension)) {
553
					return file_name_with_extension;
554
				}
555

  
556
				// Also try loading 'dot.dot.bundle' for 'dot.dot'
557
				file_name_with_extension = rb_str_plus(
558
						base_file_name,
559
						rb_str_new2(alternate_dl_extensions[j]));
560

  
561
				if (rb_feature_exists(file_name_with_extension)) {
562
					return file_name_with_extension;
563
				}
564
			}
565
		}
566
	}
567
	return Qnil;
568
}
569

  
570
static VALUE
571
rb_locate_file_absolute(VALUE fname)
572
{
573
	return rb_locate_file_with_extensions(fname);
574
}
575

  
576
static VALUE
577
rb_locate_file_relative(VALUE fname)
578
{
579
	return rb_locate_file_with_extensions(rb_file_expand_path(fname, Qnil));
580
}
581

  
582
static VALUE
583
rb_locate_file_in_load_path(VALUE path)
584
{
585
	long i, j;
586
	VALUE load_path = rb_get_expanded_load_path();
587
	VALUE expanded_file_name = Qnil;
588
	VALUE base_file_name = Qnil;
589
	VALUE sep = rb_str_new2("/");
590

  
591
	for (i = 0; i < RARRAY_LEN(load_path); ++i) {
592
		VALUE directory = RARRAY_PTR(load_path)[i];
593

  
594
		base_file_name = rb_str_plus(directory, sep);
595
		base_file_name = rb_str_concat(base_file_name, path);
596

  
597
		expanded_file_name = rb_locate_file_with_extensions(base_file_name);
598

  
599
		if (expanded_file_name != Qnil) {
600
			return expanded_file_name;
601
		}
602
	}
603
	return Qnil;
604
}
605

  
606
static int
607
rb_path_is_relative(VALUE path)
608
{
609
	const char * path_ptr = RSTRING_PTR(path);
610
	const char * current_directory = "./";
611
	const char * parent_directory  = "../";
612

  
613
	return (
614
			strncmp(current_directory, path_ptr, 2) == 0 ||
615
			strncmp(parent_directory,  path_ptr, 3) == 0
616
		   );
617
}
618

  
619
static int
620
rb_file_is_ruby(VALUE path)
621
{
622
	const char * ext;
623
	ext = ruby_find_extname(RSTRING_PTR(path), 0);
624

  
625
	return ext && IS_RBEXT(ext);
626
}
627

  
628
static int
629
rb_path_is_absolute(VALUE path)
630
{
631
	// Delegate to file.c
632
	return rb_is_absolute_path(RSTRING_PTR(path));
633
}
634

  
635
static int
636
rb_file_has_been_required(VALUE expanded_path)
637
{
638
	st_data_t data;
639
	st_data_t path_key = (st_data_t)RSTRING_PTR(expanded_path);
640
	st_table *loaded_features_hash = get_loaded_features_hash();
641

  
642
	return st_lookup(loaded_features_hash, path_key, &data);
643
}
644

  
645
static int
646
rb_file_is_being_required(VALUE full_path) {
647
	const char *ftptr = RSTRING_PTR(full_path);
648
	st_data_t data;
649
	st_table *loading_tbl = get_loading_table();
650

  
651
	return (loading_tbl && st_lookup(loading_tbl, (st_data_t)ftptr, &data));
652
}
653

  
654
static VALUE
655
rb_get_cached_expansion(VALUE filename) 
656
{
657
	st_data_t data;
658
	st_data_t path_key = (st_data_t)RSTRING_PTR(filename);
659
	st_table *filename_expansion_hash = get_filename_expansion_hash();
660

  
661
	if (st_lookup(filename_expansion_hash, path_key, &data)) {
662
		return (VALUE)data;
663
	} else {
664
		return Qnil;
665
	};
666
}
667

  
668
static void
669
rb_set_cached_expansion(VALUE filename, VALUE expanded)
670
{
671
	st_data_t data = (st_data_t)expanded;
672
	st_data_t path_key = (st_data_t)RSTRING_PTR(filename);
673
	st_table *filename_expansion_hash = get_filename_expansion_hash();
674

  
675
	st_insert(filename_expansion_hash, path_key, data);
676
}
677

  
678
static VALUE
679
rb_locate_file(VALUE filename)
680
{
681
	VALUE full_path = Qnil;
682

  
683
	full_path = rb_get_cached_expansion(filename);
684

  
685
	if (full_path != Qnil)
686
		return full_path;
687

  
688
	if (rb_path_is_relative(filename)) {
689
		full_path = rb_locate_file_relative(filename);
690
	} else if (rb_path_is_absolute(filename)) {
691
		full_path = rb_locate_file_absolute(filename);
692
	} else {
693
		full_path = rb_locate_file_in_load_path(filename);
694
	}
695

  
696
	if (full_path != Qnil)
697
		rb_set_cached_expansion(filename, full_path);
698

  
699
	return full_path;
700
}
701

  
702
/* 
703
 * returns the path loaded, or nil if the file was already loaded. Raises
704
 * LoadError if a file cannot be found. 
705
 */
706
VALUE
707
rb_require_safe(VALUE fname, int safe)
708
{
709
	VALUE path = Qnil;
710
	volatile VALUE result = Qnil;
711
	rb_thread_t *th = GET_THREAD();
712
	volatile VALUE errinfo = th->errinfo;
713
	int state;
714
	struct {
715
		int safe;
716
	} volatile saved;
717
	char *volatile ftptr = 0;
718

  
719
	PUSH_TAG();
720
	saved.safe = rb_safe_level();
721
	if ((state = EXEC_TAG()) == 0) {
722
		long handle;
723
		int found;
724

  
725
		rb_set_safe_level_force(safe);
726
		FilePathValue(fname);
727
		rb_set_safe_level_force(0);
728

  
729
		path = rb_locate_file(fname);
730

  
731
		if (safe >= 1 && OBJ_TAINTED(path)) {
732
			rb_raise(rb_eSecurityError, "Loading from unsafe file %s", RSTRING_PTR(path));
733
		}
734

  
735
		result = Qfalse;
736
		if (path == Qnil) {
737
			load_failed(fname);
738
		} else {
739
			if (ftptr = load_lock(RSTRING_PTR(path))) { // Allows circular requires to work
740
				if (!rb_file_has_been_required(path)) {
741
					if (rb_file_is_ruby(path)) {
742
						rb_load_internal(path, 0);
743
					} else {
744
						handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
745
								path, 0, path);
746
						rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
747
					}
748
					rb_provide_feature(path);
749
					result = Qtrue;
750
				}
751
			}
752
		}
753
	}
754
	POP_TAG();
755
	load_unlock(ftptr, !state);
756

  
757
	rb_set_safe_level_force(saved.safe);
758
	if (state) {
759
		JUMP_TAG(state);
760
	}
761

  
762
	if (NIL_P(result)) {
763
		load_failed(fname);
764
	}
765

  
766
	th->errinfo = errinfo;
767

  
768
	if (result == Qtrue) {
769
		return path;
770
	} else {
771
		return Qnil;
772
	}
773
}
774

  
775
/*
776
 *  call-seq:
777
 *     require(string)    -> true or false
778
 *
779
 *  Ruby tries to load the library named _string_, returning
780
 *  +true+ if successful. If the filename does not resolve to
781
 *  an absolute path, it will be searched for in the directories listed
782
 *  in <code>$:</code>. If the file has the extension ``.rb'', it is
783
 *  loaded as a source file; if the extension is ``.so'', ``.o'', or
784
 *  ``.dll'', or whatever the default shared library extension is on
785
 *  the current platform, Ruby loads the shared library as a Ruby
786
 *  extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on
787
 *  to the name. The name of the loaded feature is added to the array in
788
 *  <code>$"</code>. A feature will not be loaded if its name already
789
 *  appears in <code>$"</code>. The file name is converted to an absolute
790
 *  path, so ``<code>require 'a'; require './a'</code>'' will not load
791
 *  <code>a.rb</code> twice.
792
 *
793
 *     require "my-library.rb"
794
 *     require "db-driver"
795
 */
796
VALUE
797
rb_f_require(VALUE obj, VALUE fname)
798
{
799
    return rb_require_safe(fname, rb_safe_level()) == Qnil ? Qfalse : Qtrue;
800
}
801

  
802
static void
803
rb_rehash_loaded_features()
804
{
805
  int i;
806
  VALUE features;
807
  VALUE feature;
808

  
809
  st_table* loaded_features_hash = get_loaded_features_hash();
810

  
811
  st_clear(loaded_features_hash);
812

  
813
  features = get_loaded_features();
814

  
815
  for (i = 0; i < RARRAY_LEN(features); ++i) {
816
    feature = RARRAY_PTR(features)[i];
817
    st_insert(
818
      loaded_features_hash,
819
      (st_data_t)ruby_strdup(RSTRING_PTR(feature)),
820
      (st_data_t)rb_barrier_new());
821
  }
822
}
823

  
824
static void
825
rb_clear_cached_expansions()
826
{
827
  st_table* filename_expansion_hash = get_filename_expansion_hash();
828
  st_clear(filename_expansion_hash);
829
}
830

  
831
static VALUE  
832
rb_loaded_features_hook(int argc, VALUE *argv, VALUE self)  
833
{ 
834
	VALUE ret;
835
	ret = rb_call_super(argc, argv);
836
	rb_rehash_loaded_features();
837
	rb_clear_cached_expansions();
838
	return ret;
839
}
840

  
841
/*
842
 * $LOADED_FEATURES is exposed publically as an array, but under the covers
843
 * we also store this data in a hash for fast lookups. So that we can rebuild
844
 * the hash whenever $LOADED_FEATURES is changed, we wrap the Array class
845
 * in a proxy that intercepts all data-modifying methods and rebuilds the
846
 * hash.
847
 *
848
 * Note that the list of intercepted methods is currently non-comprehensive
849
 * --- it only covers modifications made by the ruby and rubyspec test suites.
850
 */
851
static void 
852
define_loaded_features_proxy()
853
{
854
	const char* methods_to_hook[] = {"push", "clear", "replace", "delete"};
855
	unsigned int i;
856

  
857
	rb_cLoadedFeaturesProxy = rb_define_class("LoadedFeaturesProxy", rb_cArray); 
858
	for (i = 0; i < CHAR_ARRAY_LEN(methods_to_hook); ++i) {
859
		rb_define_method(
860
				rb_cLoadedFeaturesProxy,
861
				methods_to_hook[i],
862
				rb_loaded_features_hook,
863
				-1);
864
	}
865
}
866

  
867

  
868
/* Should return true if the file has or is being loaded, but should 
869
 * not actually load the file.
870
 */
871
int
872
rb_feature_provided_2(VALUE fname)
873
{
874
	VALUE full_path = rb_locate_file(fname);
875

  
876
	if (
877
		full_path != Qnil && 
878
		(
879
			rb_file_has_been_required(full_path) || 
880
			rb_file_is_being_required(full_path)
881
		)
882
	) {
883
		return TRUE;
884
	} else {
885
		return FALSE;
886
	}
887
}
888

  
889
/*
890
 * Deprecated, use rb_feature_provided_2
891
 */
892
int
893
rb_feature_provided(const char *feature, const char **loading)
894
{
895
    VALUE fname = rb_str_new2(feature);
896
	rb_feature_provided_2(fname);
897
}
898

  
899

  
744 900
void
745 901
Init_load()
746 902
{
747 903
#undef rb_intern
748 904
#define rb_intern(str) rb_intern2((str), strlen(str))
905
	unsigned int j;
749 906
    rb_vm_t *vm = GET_VM();
750 907
    static const char var_load_path[] = "$:";
751 908
    ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1);
......
757 914

  
758 915
    rb_define_virtual_variable("$\"", get_loaded_features, 0);
759 916
    rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
760
    vm->loaded_features = rb_ary_new();
917

  
918
    define_loaded_features_proxy();
919

  
920
    vm->loaded_features = ary_new(rb_cLoadedFeaturesProxy, RARRAY_EMBED_LEN_MAX);
761 921

  
762 922
    rb_define_global_function("load", rb_f_load, -1);
763 923
    rb_define_global_function("require", rb_f_require, 1);
......
769 929

  
770 930
    ruby_dln_librefs = rb_ary_new();
771 931
    rb_gc_register_mark_object(ruby_dln_librefs);
932

  
933
	for (j = 0; j < CHAR_ARRAY_LEN(available_extensions); ++j) {
934
		available_ext_rb_str[j] = rb_str_new2(available_extensions[j]);
935
		rb_gc_register_mark_object(available_ext_rb_str[j]);
936
	}
772 937
}
test/ruby/test_require.rb
339 339
                      [], /\$LOADED_FEATURES is frozen; cannot append feature \(RuntimeError\)$/,
340 340
                      bug3756)
341 341
  end
342

  
343
  def test_case_insensitive
344
    load_path = $:.dup
345
    loaded = $".dup
346
    path = File.expand_path(__FILE__)
347
    $:.unshift(File.dirname(path))
348
    $".push(path) unless $".include?(path)
349
    bug4255 = '[ruby-core:34297]'
350
    assert_equal(false, $bug4255 ||= false, bug4255)
351
    $bug4255 = true
352
    f = File.basename(__FILE__, ".*").upcase
353
    assert_equal(false, require(f))
354
  ensure
355
    $:.replace(load_path)
356
    $".replace(loaded)
357
  end if File.identical?(__FILE__, __FILE__.upcase)
358

  
359
  def test_feature_is_reloaded_from_new_load_path_entry
360
    # This is a bit of a weird test, but it is needed to ensure that some
361
    # caching optimizations are working correctly.
362
    load_path      = $:.dup
363
    loaded         = $".dup
364
    initial_length = loaded.length
365

  
366
    Dir.mktmpdir do |tmp|
367
      Dir.chdir(tmp) do
368
        Dir.mkdir "a"
369
        Dir.mkdir "b"
370
        File.open("a/test.rb", "w") {|f| f.puts '' }
371
        File.open("b/test.rb", "w") {|f| f.puts '' }
372

  
373
        $".clear
374
        $:.unshift(File.join(tmp, "b"))
375
        require 'test.rb'
376
        assert $"[0].include?('b/test.rb')
377

  
378
        $".clear
379
        $:.unshift(File.join(tmp, "a"))
380
        require 'test.rb'
381
        assert $"[0].include?('a/test.rb')
382
      end
383
    end
384
  ensure
385
    $:.replace(load_path)
386
    $".replace(loaded)
387
  end
342 388
end
variable.c
1489 1489
}
1490 1490

  
1491 1491
static VALUE
1492
autoload_provided(VALUE arg)
1492
autoload_provided(VALUE fname)
1493 1493
{
1494
    const char **p = (const char **)arg;
1495
    return rb_feature_provided(*p, p);
1494
    return rb_feature_provided_2(fname);
1496 1495
}
1497 1496

  
1498 1497
static VALUE
......
1525 1524
    loading = RSTRING_PTR(file);
1526 1525
    safe = rb_safe_level();
1527 1526
    rb_set_safe_level_force(0);
1528
    if (!rb_ensure(autoload_provided, (VALUE)&loading, reset_safe, (VALUE)safe)) {
1527
    if (!rb_ensure(autoload_provided, (VALUE)file, reset_safe, (VALUE)safe)) {
1529 1528
	return load;
1530 1529
    }
1531 1530
    if (loadingpath && loading) {
vm.c
1527 1527
	    rb_mark_tbl(vm->loading_table);
1528 1528
	}
1529 1529

  
1530
	if (vm->loaded_features_hash) {
1531
	    rb_mark_tbl(vm->loaded_features_hash);
1532
	}
1533

  
1534
	if (vm->filename_expansion_hash) {
1535
	    rb_mark_tbl(vm->filename_expansion_hash);
1536
	}
1537

  
1530 1538
	mark_event_hooks(vm->event_hooks);
1531 1539

  
1532 1540
	for (i = 0; i < RUBY_NSIG; i++) {
vm_core.h
324 324
     * objects so do *NOT* mark this when you GC.
325 325
     */
326 326
    struct RArray at_exit;
327

  
328
    struct st_table *loaded_features_hash;
329
    struct st_table *filename_expansion_hash;
327 330
} rb_vm_t;
328 331

  
329 332
typedef struct {