Created
July 7, 2011 22:24
-
-
Save daviddavis/1070688 to your computer and use it in GitHub Desktop.
Ruby 1.9.2 patch to speed up require
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/array.c b/array.c | |
index b1616c5..16326fc 100644 | |
--- a/array.c | |
+++ b/array.c | |
@@ -302,7 +302,7 @@ ary_alloc(VALUE klass) | |
return (VALUE)ary; | |
} | |
-static VALUE | |
+VALUE | |
ary_new(VALUE klass, long capa) | |
{ | |
VALUE ary; | |
diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb | |
index 107a387..90cc3b7 100755 | |
--- a/enc/make_encmake.rb | |
+++ b/enc/make_encmake.rb | |
@@ -3,10 +3,8 @@ | |
dir = File.expand_path("../..", __FILE__) | |
$:.unshift(dir) | |
$:.unshift(".") | |
-if $".grep(/mkmf/).empty? | |
- $" << "mkmf.rb" | |
- load File.expand_path("lib/mkmf.rb", dir) | |
-end | |
+$:.unshift( dir + '/lib' ) | |
+require 'mkmf' | |
require 'erb' | |
CONFIG["MAKEDIRS"] ||= '@$(MINIRUBY) -run -e mkdir -- -p' | |
diff --git a/enumerator.c b/enumerator.c | |
index f060601..d085e9b 100644 | |
--- a/enumerator.c | |
+++ b/enumerator.c | |
@@ -1115,6 +1115,4 @@ Init_Enumerator(void) | |
id_rewind = rb_intern("rewind"); | |
id_each = rb_intern("each"); | |
sym_each = ID2SYM(id_each); | |
- | |
- rb_provide("enumerator.so"); /* for backward compatibility */ | |
} | |
diff --git a/ext/extmk.rb b/ext/extmk.rb | |
index ab48634..57f7fea 100644 | |
--- a/ext/extmk.rb | |
+++ b/ext/extmk.rb | |
@@ -35,8 +35,8 @@ require 'rbconfig' | |
$topdir = "." | |
$top_srcdir = srcdir | |
-$" << "mkmf.rb" | |
-load File.expand_path("lib/mkmf.rb", srcdir) | |
+$:.unshift( srcdir + '/lib' ) | |
+require 'mkmf' | |
require 'optparse/shellwords' | |
def sysquote(x) | |
diff --git a/file.c b/file.c | |
index 0cae95b..87b1902 100644 | |
--- a/file.c | |
+++ b/file.c | |
@@ -1254,7 +1254,7 @@ rb_file_chardev_p(VALUE obj, VALUE fname) | |
* Return <code>true</code> if the named file exists. | |
*/ | |
-static VALUE | |
+VALUE | |
rb_file_exist_p(VALUE obj, VALUE fname) | |
{ | |
struct stat st; | |
diff --git a/lib/enumerator.rb b/lib/enumerator.rb | |
new file mode 100644 | |
index 0000000..b028c83 | |
--- /dev/null | |
+++ b/lib/enumerator.rb | |
@@ -0,0 +1,3 @@ | |
+# This class is now defined entirely in enumerator.c and is always available. | |
+# This file needs to remain here for backwards compatibility, so that `require | |
+# "enumerator"` will not raise an exception. | |
diff --git a/load.c b/load.c | |
index f8f0551..82dc9bd 100644 | |
--- a/load.c | |
+++ b/load.c | |
@@ -17,15 +17,63 @@ VALUE ruby_dln_librefs; | |
#define IS_DLEXT(e) (strcmp(e, DLEXT) == 0) | |
#endif | |
+VALUE rb_f_require(VALUE, VALUE); | |
+VALUE rb_f_require_relative(VALUE, VALUE); | |
+static VALUE rb_f_load(int, VALUE *); | |
+VALUE rb_require_safe(VALUE, int); | |
+static int rb_file_has_been_required(VALUE); | |
+static int rb_file_is_being_required(VALUE); | |
+static int rb_file_is_ruby(VALUE); | |
+static st_table * get_loaded_features_hash(void); | |
+static void rb_load_internal(VALUE, int); | |
+static char * load_lock(const char *); | |
+static void load_unlock(const char *, int); | |
+static void load_failed(VALUE fname); | |
+ | |
+void rb_provide(const char *feature); | |
+static void rb_provide_feature(VALUE); | |
+static VALUE rb_locate_file(VALUE); | |
+static VALUE rb_locate_file_absolute(VALUE); | |
+static VALUE rb_locate_file_relative(VALUE); | |
+static VALUE rb_locate_file_in_load_path(VALUE); | |
+static VALUE rb_locate_file_with_extensions(VALUE, VALUE); | |
+static int rb_path_is_absolute(VALUE); | |
+static int rb_path_is_relative(VALUE); | |
+VALUE rb_get_expanded_load_path(); | |
+static VALUE rb_cLoadedFeaturesProxy; | |
+static void rb_rehash_loaded_features(); | |
+static VALUE rb_loaded_features_hook(int, VALUE*, VALUE); | |
+static void define_loaded_features_proxy(); | |
+ | |
+VALUE ary_new(VALUE, long); // array.c | |
+VALUE rb_file_exist_p(VALUE, VALUE); // file.c | |
+ | |
+const char *available_extensions[] = { | |
+ ".rb", | |
+ DLEXT, | |
+#ifdef DLEXT2 | |
+ DLEXT2, | |
+#endif | |
+ "" | |
+}; | |
+extern const char *ruby_find_extname(const char *, long *); | |
-static const char *const loadable_ext[] = { | |
- ".rb", DLEXT, | |
#ifdef DLEXT2 | |
- DLEXT2, | |
+VALUE available_ext_rb_str[4]; | |
+#else | |
+VALUE available_ext_rb_str[3]; | |
+#endif | |
+ | |
+const char *alternate_dl_extensions[] = { | |
+ DLEXT, | |
+#ifdef DLEXT2 | |
+ DLEXT2 | |
#endif | |
- 0 | |
}; | |
+#define CHAR_ARRAY_LEN(array) (sizeof(array) / sizeof(char*)) | |
+#define VALUE_ARRAY_LEN(array) (sizeof(array) / sizeof(VALUE)) | |
+ | |
VALUE | |
rb_get_load_path(void) | |
{ | |
@@ -33,6 +81,27 @@ rb_get_load_path(void) | |
return load_path; | |
} | |
+static st_table * | |
+get_loaded_features_hash( void ) | |
+{ | |
+ st_table* loaded_features_hash; | |
+ loaded_features_hash = GET_VM()->loaded_features_hash; | |
+ if (!loaded_features_hash) { | |
+ GET_VM()->loaded_features_hash = loaded_features_hash = st_init_strcasetable(); | |
+ } | |
+ return loaded_features_hash; | |
+} | |
+static st_table * | |
+get_filename_expansion_hash(void) | |
+{ | |
+ st_table* filename_expansion_hash; | |
+ filename_expansion_hash = GET_VM()->filename_expansion_hash; | |
+ if (!filename_expansion_hash) { | |
+ GET_VM()->filename_expansion_hash = filename_expansion_hash = st_init_strcasetable(); | |
+ } | |
+ return filename_expansion_hash; | |
+} | |
+ | |
VALUE | |
rb_get_expanded_load_path(void) | |
{ | |
@@ -76,182 +145,29 @@ get_loading_table(void) | |
return GET_VM()->loading_table; | |
} | |
-static VALUE | |
-loaded_feature_path(const char *name, long vlen, const char *feature, long len, | |
- int type, VALUE load_path) | |
-{ | |
- long i; | |
- | |
- for (i = 0; i < RARRAY_LEN(load_path); ++i) { | |
- VALUE p = RARRAY_PTR(load_path)[i]; | |
- const char *s = StringValuePtr(p); | |
- long n = RSTRING_LEN(p); | |
- | |
- if (vlen < n + len + 1) continue; | |
- if (n && (strncmp(name, s, n) || name[n] != '/')) continue; | |
- if (strncmp(name + n + 1, feature, len)) continue; | |
- if (name[n+len+1] && name[n+len+1] != '.') continue; | |
- switch (type) { | |
- case 's': | |
- if (IS_DLEXT(&name[n+len+1])) return p; | |
- break; | |
- case 'r': | |
- if (IS_RBEXT(&name[n+len+1])) return p; | |
- break; | |
- default: | |
- return p; | |
- } | |
- } | |
- return 0; | |
-} | |
- | |
-struct loaded_feature_searching { | |
- const char *name; | |
- long len; | |
- int type; | |
- VALUE load_path; | |
- const char *result; | |
-}; | |
- | |
-static int | |
-loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f) | |
-{ | |
- const char *s = (const char *)v; | |
- struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f; | |
- VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len, | |
- fp->type, fp->load_path); | |
- if (!p) return ST_CONTINUE; | |
- fp->result = s; | |
- return ST_STOP; | |
-} | |
- | |
-static int | |
-rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn) | |
-{ | |
- VALUE v, features, p, load_path = 0; | |
- const char *f, *e; | |
- long i, len, elen, n; | |
- st_table *loading_tbl; | |
- st_data_t data; | |
- int type; | |
- | |
- if (fn) *fn = 0; | |
- if (ext) { | |
- elen = strlen(ext); | |
- len = strlen(feature) - elen; | |
- type = rb ? 'r' : 's'; | |
- } | |
- else { | |
- len = strlen(feature); | |
- elen = 0; | |
- type = 0; | |
- } | |
- features = get_loaded_features(); | |
- for (i = 0; i < RARRAY_LEN(features); ++i) { | |
- v = RARRAY_PTR(features)[i]; | |
- f = StringValuePtr(v); | |
- if ((n = RSTRING_LEN(v)) < len) continue; | |
- if (strncmp(f, feature, len) != 0) { | |
- if (expanded) continue; | |
- if (!load_path) load_path = rb_get_expanded_load_path(); | |
- if (!(p = loaded_feature_path(f, n, feature, len, type, load_path))) | |
- continue; | |
- expanded = 1; | |
- f += RSTRING_LEN(p) + 1; | |
- } | |
- if (!*(e = f + len)) { | |
- if (ext) continue; | |
- return 'u'; | |
- } | |
- if (*e != '.') continue; | |
- if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) { | |
- return 's'; | |
- } | |
- if ((rb || !ext) && (IS_RBEXT(e))) { | |
- return 'r'; | |
- } | |
- } | |
- loading_tbl = get_loading_table(); | |
- if (loading_tbl) { | |
- f = 0; | |
- if (!expanded) { | |
- struct loaded_feature_searching fs; | |
- fs.name = feature; | |
- fs.len = len; | |
- fs.type = type; | |
- fs.load_path = load_path ? load_path : rb_get_load_path(); | |
- fs.result = 0; | |
- st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs); | |
- if ((f = fs.result) != 0) { | |
- if (fn) *fn = f; | |
- goto loading; | |
- } | |
- } | |
- if (st_get_key(loading_tbl, (st_data_t)feature, &data)) { | |
- if (fn) *fn = (const char*)data; | |
- loading: | |
- if (!ext) return 'u'; | |
- return !IS_RBEXT(ext) ? 's' : 'r'; | |
- } | |
- else { | |
- VALUE bufstr; | |
- char *buf; | |
- | |
- if (ext && *ext) return 0; | |
- bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN); | |
- buf = RSTRING_PTR(bufstr); | |
- MEMCPY(buf, feature, char, len); | |
- for (i = 0; (e = loadable_ext[i]) != 0; i++) { | |
- strlcpy(buf + len, e, DLEXT_MAXLEN + 1); | |
- if (st_get_key(loading_tbl, (st_data_t)buf, &data)) { | |
- rb_str_resize(bufstr, 0); | |
- if (fn) *fn = (const char*)data; | |
- return i ? 's' : 'r'; | |
- } | |
- } | |
- rb_str_resize(bufstr, 0); | |
- } | |
- } | |
- return 0; | |
-} | |
- | |
int | |
rb_provided(const char *feature) | |
{ | |
return rb_feature_provided(feature, 0); | |
} | |
- | |
-int | |
-rb_feature_provided(const char *feature, const char **loading) | |
+/* Mark the given feature as loaded, after it has been evaluated. */ | |
+static void | |
+rb_provide_feature( VALUE feature ) | |
{ | |
- const char *ext = strrchr(feature, '.'); | |
- volatile VALUE fullpath = 0; | |
+ int frozen = 0; | |
+ st_table* loaded_features_hash; | |
+ if( OBJ_FROZEN( get_loaded_features() ) ) { | |
+ rb_raise( rb_eRuntimeError, "$LOADED_FEATURES is frozen; cannot append feature." ); | |
+ } | |
+ loaded_features_hash = get_loaded_features_hash(); | |
+ st_insert( | |
+ loaded_features_hash, | |
+ (st_data_t)ruby_strdup(RSTRING_PTR(feature)), | |
+ (st_data_t)rb_barrier_new()); | |
- if (*feature == '.' && | |
- (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) { | |
- fullpath = rb_file_expand_path(rb_str_new2(feature), Qnil); | |
- feature = RSTRING_PTR(fullpath); | |
- } | |
- if (ext && !strchr(ext, '/')) { | |
- if (IS_RBEXT(ext)) { | |
- if (rb_feature_p(feature, ext, TRUE, FALSE, loading)) return TRUE; | |
- return FALSE; | |
- } | |
- else if (IS_SOEXT(ext) || IS_DLEXT(ext)) { | |
- if (rb_feature_p(feature, ext, FALSE, FALSE, loading)) return TRUE; | |
- return FALSE; | |
- } | |
- } | |
- if (rb_feature_p(feature, 0, TRUE, FALSE, loading)) | |
- return TRUE; | |
- return FALSE; | |
+ rb_ary_push(get_loaded_features(), feature); | |
} | |
-static void | |
-rb_provide_feature(VALUE feature) | |
-{ | |
- rb_ary_push(get_loaded_features(), feature); | |
-} | |
void | |
rb_provide(const char *feature) | |
@@ -420,34 +336,6 @@ load_unlock(const char *ftptr, int done) | |
} | |
-/* | |
- * call-seq: | |
- * require(string) -> true or false | |
- * | |
- * Ruby tries to load the library named _string_, returning | |
- * +true+ if successful. If the filename does not resolve to | |
- * an absolute path, it will be searched for in the directories listed | |
- * in <code>$:</code>. If the file has the extension ``.rb'', it is | |
- * loaded as a source file; if the extension is ``.so'', ``.o'', or | |
- * ``.dll'', or whatever the default shared library extension is on | |
- * the current platform, Ruby loads the shared library as a Ruby | |
- * extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on | |
- * to the name. The name of the loaded feature is added to the array in | |
- * <code>$"</code>. A feature will not be loaded if its name already | |
- * appears in <code>$"</code>. The file name is converted to an absolute | |
- * path, so ``<code>require 'a'; require './a'</code>'' will not load | |
- * <code>a.rb</code> twice. | |
- * | |
- * require "my-library.rb" | |
- * require "db-driver" | |
- */ | |
- | |
-VALUE | |
-rb_f_require(VALUE obj, VALUE fname) | |
-{ | |
- return rb_require_safe(fname, rb_safe_level()); | |
-} | |
- | |
VALUE | |
rb_f_require_relative(VALUE obj, VALUE fname) | |
{ | |
@@ -460,92 +348,6 @@ rb_f_require_relative(VALUE obj, VALUE fname) | |
return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level()); | |
} | |
-static int | |
-search_required(VALUE fname, volatile VALUE *path, int safe_level) | |
-{ | |
- VALUE tmp; | |
- char *ext, *ftptr; | |
- int type, ft = 0; | |
- const char *loading; | |
- | |
- *path = 0; | |
- ext = strrchr(ftptr = RSTRING_PTR(fname), '.'); | |
- if (ext && !strchr(ext, '/')) { | |
- if (IS_RBEXT(ext)) { | |
- if (rb_feature_p(ftptr, ext, TRUE, FALSE, &loading)) { | |
- if (loading) *path = rb_str_new2(loading); | |
- return 'r'; | |
- } | |
- if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) { | |
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); | |
- if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading) | |
- *path = tmp; | |
- return 'r'; | |
- } | |
- return 0; | |
- } | |
- else if (IS_SOEXT(ext)) { | |
- if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) { | |
- if (loading) *path = rb_str_new2(loading); | |
- return 's'; | |
- } | |
- tmp = rb_str_new(RSTRING_PTR(fname), ext - RSTRING_PTR(fname)); | |
-#ifdef DLEXT2 | |
- OBJ_FREEZE(tmp); | |
- if (rb_find_file_ext_safe(&tmp, loadable_ext + 1, safe_level)) { | |
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); | |
- if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) | |
- *path = tmp; | |
- return 's'; | |
- } | |
-#else | |
- rb_str_cat2(tmp, DLEXT); | |
- OBJ_FREEZE(tmp); | |
- if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) { | |
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); | |
- if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) | |
- *path = tmp; | |
- return 's'; | |
- } | |
-#endif | |
- } | |
- else if (IS_DLEXT(ext)) { | |
- if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) { | |
- if (loading) *path = rb_str_new2(loading); | |
- return 's'; | |
- } | |
- if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) { | |
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); | |
- if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) | |
- *path = tmp; | |
- return 's'; | |
- } | |
- } | |
- } | |
- else if ((ft = rb_feature_p(ftptr, 0, FALSE, FALSE, &loading)) == 'r') { | |
- if (loading) *path = rb_str_new2(loading); | |
- return 'r'; | |
- } | |
- tmp = fname; | |
- type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level); | |
- switch (type) { | |
- case 0: | |
- if (ft) | |
- break; | |
- ftptr = RSTRING_PTR(tmp); | |
- return rb_feature_p(ftptr, 0, FALSE, TRUE, 0); | |
- | |
- default: | |
- if (ft) | |
- break; | |
- case 1: | |
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); | |
- if (rb_feature_p(ftptr, ext, !--type, TRUE, &loading) && !loading) | |
- break; | |
- *path = tmp; | |
- } | |
- return type ? 's' : 'r'; | |
-} | |
static void | |
load_failed(VALUE fname) | |
@@ -562,66 +364,6 @@ load_ext(VALUE path) | |
return (VALUE)dln_load(RSTRING_PTR(path)); | |
} | |
-VALUE | |
-rb_require_safe(VALUE fname, int safe) | |
-{ | |
- volatile VALUE result = Qnil; | |
- rb_thread_t *th = GET_THREAD(); | |
- volatile VALUE errinfo = th->errinfo; | |
- int state; | |
- struct { | |
- int safe; | |
- } volatile saved; | |
- char *volatile ftptr = 0; | |
- | |
- PUSH_TAG(); | |
- saved.safe = rb_safe_level(); | |
- if ((state = EXEC_TAG()) == 0) { | |
- VALUE path; | |
- long handle; | |
- int found; | |
- | |
- rb_set_safe_level_force(safe); | |
- FilePathValue(fname); | |
- rb_set_safe_level_force(0); | |
- found = search_required(fname, &path, safe); | |
- if (found) { | |
- if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) { | |
- result = Qfalse; | |
- } | |
- else { | |
- switch (found) { | |
- case 'r': | |
- rb_load_internal(path, 0); | |
- break; | |
- | |
- case 's': | |
- handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext, | |
- path, 0, path, path); | |
- rb_ary_push(ruby_dln_librefs, LONG2NUM(handle)); | |
- break; | |
- } | |
- rb_provide_feature(path); | |
- result = Qtrue; | |
- } | |
- } | |
- } | |
- POP_TAG(); | |
- load_unlock(ftptr, !state); | |
- | |
- rb_set_safe_level_force(saved.safe); | |
- if (state) { | |
- JUMP_TAG(state); | |
- } | |
- | |
- if (NIL_P(result)) { | |
- load_failed(fname); | |
- } | |
- | |
- th->errinfo = errinfo; | |
- | |
- return result; | |
-} | |
VALUE | |
rb_require(const char *fname) | |
@@ -714,6 +456,455 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file) | |
return rb_mod_autoload(klass, sym, file); | |
} | |
+static int | |
+rb_feature_exists(VALUE expanded_path) | |
+{ | |
+ return rb_funcall(rb_cFile, rb_intern("file?"), 1, expanded_path) == Qtrue; | |
+} | |
+ | |
+static VALUE | |
+rb_locate_file_with_extension(VALUE base_file_name, VALUE extension) { | |
+ VALUE file_name_with_extension = rb_str_plus( | |
+ base_file_name, | |
+ extension); | |
+ | |
+ if (rb_feature_exists(file_name_with_extension)) { | |
+ return file_name_with_extension; | |
+ } else { | |
+ return Qnil; | |
+ } | |
+} | |
+ | |
+static VALUE | |
+rb_locate_file_with_extensions(VALUE base_file_name, VALUE extension) { | |
+ unsigned int j; | |
+ VALUE file_name_with_extension; | |
+ VALUE directory, basename; | |
+ | |
+ for (j = 0; j < VALUE_ARRAY_LEN(available_ext_rb_str); ++j) { | |
+ file_name_with_extension = rb_str_plus( | |
+ base_file_name, | |
+ available_ext_rb_str[j]); | |
+ | |
+ if (rb_feature_exists(file_name_with_extension)) { | |
+ return file_name_with_extension; | |
+ } | |
+ } | |
+ if (RSTRING_LEN(extension) != 0 && rb_feature_exists(base_file_name)) { | |
+ return base_file_name; | |
+ } else { | |
+ for (j = 0; j < CHAR_ARRAY_LEN(alternate_dl_extensions); ++j) { | |
+ // Try loading the native DLEXT version of this platform. | |
+ // This allows 'pathname.so' to require 'pathname.bundle' on OSX | |
+ directory = rb_file_dirname(base_file_name); | |
+ basename = rb_funcall(rb_cFile, rb_intern("basename"), 2, | |
+ base_file_name, extension); | |
+ basename = rb_str_cat2(basename, alternate_dl_extensions[j]); | |
+ | |
+ file_name_with_extension = rb_funcall(rb_cFile, rb_intern("join"), 2, | |
+ directory, basename); | |
+ | |
+ if (rb_feature_exists(file_name_with_extension)) { | |
+ return file_name_with_extension; | |
+ } | |
+ | |
+ // Also try loading 'dot.dot.bundle' for 'dot.dot' | |
+ file_name_with_extension = rb_str_plus( | |
+ base_file_name, | |
+ rb_str_new2(alternate_dl_extensions[j])); | |
+ | |
+ if (rb_feature_exists(file_name_with_extension)) { | |
+ return file_name_with_extension; | |
+ } | |
+ } | |
+ } | |
+ return Qnil; | |
+} | |
+ | |
+static VALUE | |
+rb_file_extension(VALUE path) | |
+{ | |
+ return rb_funcall(rb_cFile, rb_intern("extname"), 1, path); | |
+} | |
+ | |
+static VALUE | |
+rb_locate_file_absolute(VALUE fname) | |
+{ | |
+ return rb_locate_file_with_extensions(fname, rb_file_extension(fname)); | |
+} | |
+ | |
+static VALUE | |
+rb_locate_file_relative(VALUE fname) | |
+{ | |
+ VALUE path = rb_file_expand_path(fname, Qnil); | |
+ return rb_locate_file_with_extensions(path, rb_file_extension(path)); | |
+} | |
+ | |
+/* This function is only used as an optimization in rb_locate_file_in_load_path */ | |
+static VALUE | |
+rb_locate_rb_file_in_load_path(VALUE path, VALUE load_path, VALUE sep) | |
+{ | |
+ long i; | |
+ VALUE base_file_name; | |
+ VALUE expanded_file_name; | |
+ VALUE rb_ext = rb_str_new2(".rb"); | |
+ | |
+ for (i = 0; i < RARRAY_LEN(load_path); ++i) { | |
+ VALUE directory = RARRAY_PTR(load_path)[i]; | |
+ | |
+ base_file_name = rb_str_plus(directory, sep); | |
+ base_file_name = rb_str_concat(base_file_name, path); | |
+ | |
+ expanded_file_name = rb_locate_file_with_extension(base_file_name, rb_ext); | |
+ | |
+ if (expanded_file_name != Qnil) { | |
+ return expanded_file_name; | |
+ } | |
+ } | |
+ return Qnil; | |
+} | |
+ | |
+static VALUE | |
+rb_locate_file_in_load_path(VALUE path) | |
+{ | |
+ long i; | |
+ VALUE load_path = rb_get_expanded_load_path(); | |
+ VALUE expanded_file_name = Qnil; | |
+ VALUE base_file_name = Qnil; | |
+ VALUE sep = rb_str_new2("/"); | |
+ VALUE base_extension = rb_file_extension(path); | |
+ | |
+ if (RSTRING_LEN(base_extension) == 0) { | |
+ /* Do an initial loop through the load path only looking for .rb files. | |
+ * This is the most common case, so optimize for it. If not found, fall | |
+ * back so searching all extensions. | |
+ */ | |
+ expanded_file_name = rb_locate_rb_file_in_load_path(path, load_path, sep); | |
+ | |
+ if (expanded_file_name != Qnil) { | |
+ return expanded_file_name; | |
+ } | |
+ } | |
+ | |
+ for (i = 0; i < RARRAY_LEN(load_path); ++i) { | |
+ VALUE directory = RARRAY_PTR(load_path)[i]; | |
+ | |
+ base_file_name = rb_str_plus(directory, sep); | |
+ base_file_name = rb_str_concat(base_file_name, path); | |
+ | |
+ /* The .rb extension will be checked again in this call, which is redundant | |
+ * since it was checked in the loop above. This hasn't been optimized to | |
+ * keep the code cleaner. | |
+ */ | |
+ expanded_file_name = rb_locate_file_with_extensions(base_file_name, base_extension); | |
+ | |
+ if (expanded_file_name != Qnil) { | |
+ return expanded_file_name; | |
+ } | |
+ } | |
+ return Qnil; | |
+} | |
+ | |
+static int | |
+rb_path_is_relative(VALUE path) | |
+{ | |
+ const char * path_ptr = RSTRING_PTR(path); | |
+ const char * current_directory = "./"; | |
+ const char * parent_directory = "../"; | |
+ | |
+ return ( | |
+ strncmp(current_directory, path_ptr, 2) == 0 || | |
+ strncmp(parent_directory, path_ptr, 3) == 0 | |
+ ); | |
+} | |
+ | |
+static int | |
+rb_file_is_ruby(VALUE path) | |
+{ | |
+ const char * ext; | |
+ long test; | |
+ long slen; | |
+ const char *name; | |
+ | |
+ name = StringValueCStr(path); | |
+ slen = strlen(name); | |
+ ext = ruby_find_extname(name, &test); | |
+ | |
+ return ext && IS_RBEXT(ext); | |
+} | |
+ | |
+static int | |
+rb_path_is_absolute(VALUE path) | |
+{ | |
+ // Delegate to file.c | |
+ return rb_is_absolute_path(RSTRING_PTR(path)); | |
+} | |
+ | |
+static int | |
+rb_file_has_been_required(VALUE expanded_path) | |
+{ | |
+ st_data_t data; | |
+ st_data_t path_key = (st_data_t)RSTRING_PTR(expanded_path); | |
+ st_table *loaded_features_hash = get_loaded_features_hash(); | |
+ | |
+ return st_lookup(loaded_features_hash, path_key, &data); | |
+} | |
+ | |
+static int | |
+rb_file_is_being_required(VALUE full_path) { | |
+ const char *ftptr = RSTRING_PTR(full_path); | |
+ st_data_t data; | |
+ st_table *loading_tbl = get_loading_table(); | |
+ | |
+ return (loading_tbl && st_lookup(loading_tbl, (st_data_t)ftptr, &data)); | |
+} | |
+ | |
+static VALUE | |
+rb_get_cached_expansion(VALUE filename) | |
+{ | |
+ st_data_t data; | |
+ st_data_t path_key = (st_data_t)RSTRING_PTR(filename); | |
+ st_table *filename_expansion_hash = get_filename_expansion_hash(); | |
+ | |
+ if (st_lookup(filename_expansion_hash, path_key, &data)) { | |
+ return (VALUE)data; | |
+ } else { | |
+ return Qnil; | |
+ }; | |
+} | |
+ | |
+static void | |
+rb_set_cached_expansion(VALUE filename, VALUE expanded) | |
+{ | |
+ st_data_t data = (st_data_t)expanded; | |
+ st_data_t path_key = (st_data_t)RSTRING_PTR(filename); | |
+ st_table *filename_expansion_hash = get_filename_expansion_hash(); | |
+ | |
+ st_insert(filename_expansion_hash, path_key, data); | |
+} | |
+ | |
+static VALUE | |
+rb_locate_file(VALUE filename) | |
+{ | |
+ VALUE full_path = Qnil; | |
+ | |
+ full_path = rb_get_cached_expansion(filename); | |
+ | |
+ if (full_path != Qnil) | |
+ return full_path; | |
+ | |
+ if (rb_path_is_relative(filename)) { | |
+ full_path = rb_locate_file_relative(filename); | |
+ } else if (rb_path_is_absolute(filename)) { | |
+ full_path = rb_locate_file_absolute(filename); | |
+ } else { | |
+ full_path = rb_locate_file_in_load_path(filename); | |
+ } | |
+ | |
+ if (full_path != Qnil) | |
+ rb_set_cached_expansion(filename, full_path); | |
+ | |
+ return full_path; | |
+} | |
+ | |
+/* | |
+ * returns the path loaded, or nil if the file was already loaded. Raises | |
+ * LoadError if a file cannot be found. | |
+ */ | |
+VALUE | |
+rb_require_safe(VALUE fname, int safe) | |
+{ | |
+ VALUE path = Qnil; | |
+ volatile VALUE result = Qnil; | |
+ rb_thread_t *th = GET_THREAD(); | |
+ volatile VALUE errinfo = th->errinfo; | |
+ int state; | |
+ struct { | |
+ int safe; | |
+ } volatile saved; | |
+ char *volatile ftptr = 0; | |
+ | |
+ PUSH_TAG(); | |
+ saved.safe = rb_safe_level(); | |
+ if ((state = EXEC_TAG()) == 0) { | |
+ long handle; | |
+ int found; | |
+ | |
+ rb_set_safe_level_force(safe); | |
+ FilePathValue(fname); | |
+ rb_set_safe_level_force(0); | |
+ | |
+ path = rb_locate_file(fname); | |
+ | |
+ if (safe >= 1 && OBJ_TAINTED(path)) { | |
+ rb_raise(rb_eSecurityError, "Loading from unsafe file %s", RSTRING_PTR(path)); | |
+ } | |
+ | |
+ result = Qfalse; | |
+ if (path == Qnil) { | |
+ load_failed(fname); | |
+ } else { | |
+ if (ftptr = load_lock(RSTRING_PTR(path))) { // Allows circular requires to work | |
+ if (!rb_file_has_been_required(path)) { | |
+ if (rb_file_is_ruby(path)) { | |
+ rb_load_internal(path, 0); | |
+ } else { | |
+ handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext, | |
+ path, 0, path, path); | |
+ rb_ary_push(ruby_dln_librefs, LONG2NUM(handle)); | |
+ } | |
+ rb_provide_feature(path); | |
+ result = Qtrue; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ POP_TAG(); | |
+ load_unlock(ftptr, !state); | |
+ | |
+ rb_set_safe_level_force(saved.safe); | |
+ if (state) { | |
+ JUMP_TAG(state); | |
+ } | |
+ | |
+ if (NIL_P(result)) { | |
+ load_failed(fname); | |
+ } | |
+ | |
+ th->errinfo = errinfo; | |
+ | |
+ if (result == Qtrue) { | |
+ return path; | |
+ } else { | |
+ return Qnil; | |
+ } | |
+} | |
+ | |
+/* | |
+ * call-seq: | |
+ * require(string) -> true or false | |
+ * | |
+ * Ruby tries to load the library named _string_, returning | |
+ * +true+ if successful. If the filename does not resolve to | |
+ * an absolute path, it will be searched for in the directories listed | |
+ * in <code>$:</code>. If the file has the extension ``.rb'', it is | |
+ * loaded as a source file; if the extension is ``.so'', ``.o'', or | |
+ * ``.dll'', or whatever the default shared library extension is on | |
+ * the current platform, Ruby loads the shared library as a Ruby | |
+ * extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on | |
+ * to the name. The name of the loaded feature is added to the array in | |
+ * <code>$"</code>. A feature will not be loaded if its name already | |
+ * appears in <code>$"</code>. The file name is converted to an absolute | |
+ * path, so ``<code>require 'a'; require './a'</code>'' will not load | |
+ * <code>a.rb</code> twice. | |
+ * | |
+ * require "my-library.rb" | |
+ * require "db-driver" | |
+ */ | |
+VALUE | |
+rb_f_require(VALUE obj, VALUE fname) | |
+{ | |
+ return rb_require_safe(fname, rb_safe_level()) == Qnil ? Qfalse : Qtrue; | |
+} | |
+ | |
+static void | |
+rb_rehash_loaded_features() | |
+{ | |
+ int i; | |
+ VALUE features; | |
+ VALUE feature; | |
+ | |
+ st_table* loaded_features_hash = get_loaded_features_hash(); | |
+ | |
+ st_clear(loaded_features_hash); | |
+ | |
+ features = get_loaded_features(); | |
+ | |
+ for (i = 0; i < RARRAY_LEN(features); ++i) { | |
+ feature = RARRAY_PTR(features)[i]; | |
+ st_insert( | |
+ loaded_features_hash, | |
+ (st_data_t)ruby_strdup(RSTRING_PTR(feature)), | |
+ (st_data_t)rb_barrier_new()); | |
+ } | |
+} | |
+ | |
+static void | |
+rb_clear_cached_expansions() | |
+{ | |
+ st_table* filename_expansion_hash = get_filename_expansion_hash(); | |
+ st_clear(filename_expansion_hash); | |
+} | |
+ | |
+static VALUE | |
+rb_loaded_features_hook(int argc, VALUE *argv, VALUE self) | |
+{ | |
+ VALUE ret; | |
+ ret = rb_call_super(argc, argv); | |
+ rb_rehash_loaded_features(); | |
+ rb_clear_cached_expansions(); | |
+ return ret; | |
+} | |
+ | |
+/* | |
+ * $LOADED_FEATURES is exposed publically as an array, but under the covers | |
+ * we also store this data in a hash for fast lookups. So that we can rebuild | |
+ * the hash whenever $LOADED_FEATURES is changed, we wrap the Array class | |
+ * in a proxy that intercepts all data-modifying methods and rebuilds the | |
+ * hash. | |
+ * | |
+ * Note that the list of intercepted methods is currently non-comprehensive | |
+ * --- it only covers modifications made by the ruby and rubyspec test suites. | |
+ */ | |
+static void | |
+define_loaded_features_proxy() | |
+{ | |
+ const char* methods_to_hook[] = {"<<", "push", "clear", "replace", "delete"}; | |
+ unsigned int i; | |
+ | |
+ rb_cLoadedFeaturesProxy = rb_define_class("LoadedFeaturesProxy", rb_cArray); | |
+ for (i = 0; i < CHAR_ARRAY_LEN(methods_to_hook); ++i) { | |
+ rb_define_method( | |
+ rb_cLoadedFeaturesProxy, | |
+ methods_to_hook[i], | |
+ rb_loaded_features_hook, | |
+ -1); | |
+ } | |
+} | |
+ | |
+ | |
+/* Should return true if the file has or is being loaded, but should | |
+ * not actually load the file. | |
+ */ | |
+int | |
+rb_feature_provided_2(VALUE fname) | |
+{ | |
+ VALUE full_path = rb_locate_file(fname); | |
+ | |
+ if ( | |
+ full_path != Qnil && | |
+ ( | |
+ rb_file_has_been_required(full_path) || | |
+ rb_file_is_being_required(full_path) | |
+ ) | |
+ ) { | |
+ return TRUE; | |
+ } else { | |
+ return FALSE; | |
+ } | |
+} | |
+ | |
+/* | |
+ * Deprecated, use rb_feature_provided_2 | |
+ */ | |
+int | |
+rb_feature_provided(const char *feature, const char **loading) | |
+{ | |
+ VALUE fname = rb_str_new2(feature); | |
+ rb_feature_provided_2(fname); | |
+} | |
+ | |
+ | |
/* | |
* call-seq: | |
* autoload?(name) -> String or nil | |
@@ -741,6 +932,7 @@ Init_load() | |
{ | |
#undef rb_intern | |
#define rb_intern(str) rb_intern2(str, strlen(str)) | |
+ unsigned int j; | |
rb_vm_t *vm = GET_VM(); | |
static const char var_load_path[] = "$:"; | |
ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1); | |
@@ -752,8 +944,8 @@ Init_load() | |
rb_define_virtual_variable("$\"", get_loaded_features, 0); | |
rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0); | |
- vm->loaded_features = rb_ary_new(); | |
- | |
+ define_loaded_features_proxy(); | |
+ vm->loaded_features = ary_new( rb_cLoadedFeaturesProxy, RARRAY_EMBED_LEN_MAX); | |
rb_define_global_function("load", rb_f_load, -1); | |
rb_define_global_function("require", rb_f_require, 1); | |
rb_define_global_function("require_relative", rb_f_require_relative, 1); | |
@@ -764,4 +956,9 @@ Init_load() | |
ruby_dln_librefs = rb_ary_new(); | |
rb_gc_register_mark_object(ruby_dln_librefs); | |
+ for( j =0 ; j < CHAR_ARRAY_LEN( available_extensions ); ++j ) | |
+ { | |
+ available_ext_rb_str[j]= rb_str_new2( available_extensions[j] ); | |
+ rb_gc_register_mark_object(available_ext_rb_str[j]); | |
+ } | |
} | |
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb | |
index cceae19..b340a26 100644 | |
--- a/test/ruby/test_require.rb | |
+++ b/test/ruby/test_require.rb | |
@@ -306,4 +306,49 @@ class TestRequire < Test::Unit::TestCase | |
} | |
} | |
end | |
+ def test_case_insensitive | |
+ load_path = $:.dup | |
+ loaded = $".dup | |
+ path = File.expand_path(__FILE__) | |
+ $:.unshift(File.dirname(path)) | |
+ $".push(path) unless $".include?(path) | |
+ bug4255 = '[ruby-core:34297]' | |
+ assert_equal(false, $bug4255 ||= false, bug4255) | |
+ $bug4255 = true | |
+ f = File.basename(__FILE__, ".*").upcase | |
+ assert_equal(false, require(f)) | |
+ ensure | |
+ $:.replace(load_path) | |
+ $".replace(loaded) | |
+ end if File.identical?(__FILE__, __FILE__.upcase) | |
+ | |
+ def test_feature_is_reloaded_from_new_load_path_entry | |
+ # This is a bit of a weird test, but it is needed to ensure that some | |
+ # caching optimizations are working correctly. | |
+ load_path = $:.dup | |
+ loaded = $".dup | |
+ initial_length = loaded.length | |
+ | |
+ Dir.mktmpdir do |tmp| | |
+ Dir.chdir(tmp) do | |
+ Dir.mkdir "a" | |
+ Dir.mkdir "b" | |
+ File.open("a/test.rb", "w") {|f| f.puts '' } | |
+ File.open("b/test.rb", "w") {|f| f.puts '' } | |
+ | |
+ $".clear | |
+ $:.unshift(File.join(tmp, "b")) | |
+ require 'test.rb' | |
+ assert $"[0].include?('b/test.rb') | |
+ | |
+ $".clear | |
+ $:.unshift(File.join(tmp, "a")) | |
+ require 'test.rb' | |
+ assert $"[0].include?('a/test.rb') | |
+ end | |
+ end | |
+ ensure | |
+ $:.replace(load_path) | |
+ $".replace(loaded) | |
+ end | |
end | |
diff --git a/variable.c b/variable.c | |
index 453f798..a0e4720 100644 | |
--- a/variable.c | |
+++ b/variable.c | |
@@ -20,6 +20,7 @@ | |
void rb_vm_change_state(void); | |
void rb_vm_inc_const_missing_count(void); | |
+VALUE rb_feature_provided_2( VALUE ); | |
st_table *rb_global_tbl; | |
st_table *rb_class_tbl; | |
static ID autoload, classpath, tmp_classpath, classid; | |
@@ -1480,8 +1481,7 @@ autoload_delete(VALUE mod, ID id) | |
static VALUE | |
autoload_provided(VALUE arg) | |
{ | |
- const char **p = (const char **)arg; | |
- return rb_feature_provided(*p, p); | |
+ return rb_feature_provided_2( arg ); | |
} | |
static VALUE | |
@@ -1514,7 +1514,7 @@ autoload_node(VALUE mod, ID id, const char **loadingpath) | |
loading = RSTRING_PTR(file); | |
safe = rb_safe_level(); | |
rb_set_safe_level_force(0); | |
- if (!rb_ensure(autoload_provided, (VALUE)&loading, reset_safe, (VALUE)safe)) { | |
+ if (!rb_ensure(autoload_provided, (VALUE)file, reset_safe, (VALUE)safe)) { | |
return load; | |
} | |
if (loadingpath && loading) { | |
diff --git a/vm.c b/vm.c | |
index 295dcee..5c1d1f9 100644 | |
--- a/vm.c | |
+++ b/vm.c | |
@@ -1510,6 +1510,12 @@ rb_vm_mark(void *ptr) | |
if (vm->loading_table) { | |
rb_mark_tbl(vm->loading_table); | |
} | |
+ if (vm->loaded_features_hash) { | |
+ rb_mark_tbl( vm->loaded_features_hash ); | |
+ } | |
+ if (vm->filename_expansion_hash) { | |
+ rb_mark_tbl(vm->filename_expansion_hash); | |
+ } | |
mark_event_hooks(vm->event_hooks); | |
diff --git a/vm_core.h b/vm_core.h | |
index 0fabe3e..4f05869 100644 | |
--- a/vm_core.h | |
+++ b/vm_core.h | |
@@ -316,6 +316,8 @@ typedef struct rb_vm_struct { | |
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE | |
struct rb_objspace *objspace; | |
#endif | |
+ struct st_table *loaded_features_hash; | |
+ struct st_table *filename_expansion_hash; | |
} rb_vm_t; | |
typedef struct { | |
-- | |
1.7.4.1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment