00001
00002
00003
00004
00005 #include "ruby/ruby.h"
00006 #include "ruby/util.h"
00007 #include "internal.h"
00008 #include "dln.h"
00009 #include "eval_intern.h"
00010 #include "probes.h"
00011 #include "node.h"
00012
00013 VALUE ruby_dln_librefs;
00014
00015 #define IS_RBEXT(e) (strcmp((e), ".rb") == 0)
00016 #define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0)
00017 #ifdef DLEXT2
00018 #define IS_DLEXT(e) (strcmp((e), DLEXT) == 0 || strcmp((e), DLEXT2) == 0)
00019 #else
00020 #define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
00021 #endif
00022
00023 static const char *const loadable_ext[] = {
00024 ".rb", DLEXT,
00025 #ifdef DLEXT2
00026 DLEXT2,
00027 #endif
00028 0
00029 };
00030
00031 VALUE
00032 rb_get_load_path(void)
00033 {
00034 VALUE load_path = GET_VM()->load_path;
00035 return load_path;
00036 }
00037
00038 enum expand_type {
00039 EXPAND_ALL,
00040 EXPAND_RELATIVE,
00041 EXPAND_HOME,
00042 EXPAND_NON_CACHE
00043 };
00044
00045
00046
00047
00048
00049
00050 static void
00051 rb_construct_expanded_load_path(int type, int *has_relative, int *has_non_cache)
00052 {
00053 rb_vm_t *vm = GET_VM();
00054 VALUE load_path = vm->load_path;
00055 VALUE expanded_load_path = vm->expanded_load_path;
00056 VALUE ary;
00057 long i;
00058 int level = rb_safe_level();
00059
00060 ary = rb_ary_tmp_new(RARRAY_LEN(load_path));
00061 for (i = 0; i < RARRAY_LEN(load_path); ++i) {
00062 VALUE path, as_str, expanded_path;
00063 int is_string, non_cache;
00064 char *as_cstr;
00065 as_str = path = RARRAY_AREF(load_path, i);
00066 is_string = RB_TYPE_P(path, T_STRING) ? 1 : 0;
00067 non_cache = !is_string ? 1 : 0;
00068 as_str = rb_get_path_check_to_string(path, level);
00069 as_cstr = RSTRING_PTR(as_str);
00070
00071 if (!non_cache) {
00072 if ((type == EXPAND_RELATIVE &&
00073 rb_is_absolute_path(as_cstr)) ||
00074 (type == EXPAND_HOME &&
00075 (!as_cstr[0] || as_cstr[0] != '~')) ||
00076 (type == EXPAND_NON_CACHE)) {
00077
00078 rb_ary_push(ary, RARRAY_AREF(expanded_load_path, i));
00079 continue;
00080 }
00081 }
00082 if (!*has_relative && !rb_is_absolute_path(as_cstr))
00083 *has_relative = 1;
00084 if (!*has_non_cache && non_cache)
00085 *has_non_cache = 1;
00086
00087 if (is_string)
00088 rb_str_freeze(path);
00089 as_str = rb_get_path_check_convert(path, as_str, level);
00090 expanded_path = rb_file_expand_path_fast(as_str, Qnil);
00091 rb_str_freeze(expanded_path);
00092 rb_ary_push(ary, expanded_path);
00093 }
00094 rb_obj_freeze(ary);
00095 vm->expanded_load_path = ary;
00096 rb_ary_replace(vm->load_path_snapshot, vm->load_path);
00097 }
00098
00099 static VALUE
00100 load_path_getcwd(void)
00101 {
00102 char *cwd = my_getcwd();
00103 VALUE cwd_str = rb_filesystem_str_new_cstr(cwd);
00104 xfree(cwd);
00105 return cwd_str;
00106 }
00107
00108 VALUE
00109 rb_get_expanded_load_path(void)
00110 {
00111 rb_vm_t *vm = GET_VM();
00112 const VALUE non_cache = Qtrue;
00113
00114 if (!rb_ary_shared_with_p(vm->load_path_snapshot, vm->load_path)) {
00115
00116 int has_relative = 0, has_non_cache = 0;
00117 rb_construct_expanded_load_path(EXPAND_ALL, &has_relative, &has_non_cache);
00118 if (has_relative) {
00119 vm->load_path_check_cache = load_path_getcwd();
00120 }
00121 else if (has_non_cache) {
00122
00123 vm->load_path_check_cache = non_cache;
00124 }
00125 else {
00126 vm->load_path_check_cache = 0;
00127 }
00128 }
00129 else if (vm->load_path_check_cache == non_cache) {
00130 int has_relative = 1, has_non_cache = 1;
00131
00132 rb_construct_expanded_load_path(EXPAND_NON_CACHE,
00133 &has_relative, &has_non_cache);
00134 }
00135 else if (vm->load_path_check_cache) {
00136 int has_relative = 1, has_non_cache = 1;
00137 VALUE cwd = load_path_getcwd();
00138 if (!rb_str_equal(vm->load_path_check_cache, cwd)) {
00139
00140
00141 vm->load_path_check_cache = cwd;
00142 rb_construct_expanded_load_path(EXPAND_RELATIVE,
00143 &has_relative, &has_non_cache);
00144 }
00145 else {
00146
00147 rb_construct_expanded_load_path(EXPAND_HOME,
00148 &has_relative, &has_non_cache);
00149 }
00150 }
00151 return vm->expanded_load_path;
00152 }
00153
00154 static VALUE
00155 load_path_getter(ID id, rb_vm_t *vm)
00156 {
00157 return vm->load_path;
00158 }
00159
00160 static VALUE
00161 get_loaded_features(void)
00162 {
00163 return GET_VM()->loaded_features;
00164 }
00165
00166 static void
00167 reset_loaded_features_snapshot(void)
00168 {
00169 rb_vm_t *vm = GET_VM();
00170 rb_ary_replace(vm->loaded_features_snapshot, vm->loaded_features);
00171 }
00172
00173 static struct st_table *
00174 get_loaded_features_index_raw(void)
00175 {
00176 return GET_VM()->loaded_features_index;
00177 }
00178
00179 static st_table *
00180 get_loading_table(void)
00181 {
00182 return GET_VM()->loading_table;
00183 }
00184
00185 static void
00186 features_index_add_single(VALUE short_feature, VALUE offset)
00187 {
00188 struct st_table *features_index;
00189 VALUE this_feature_index = Qnil;
00190 char *short_feature_cstr;
00191
00192 Check_Type(offset, T_FIXNUM);
00193 Check_Type(short_feature, T_STRING);
00194 short_feature_cstr = StringValueCStr(short_feature);
00195
00196 features_index = get_loaded_features_index_raw();
00197 st_lookup(features_index, (st_data_t)short_feature_cstr, (st_data_t *)&this_feature_index);
00198
00199 if (NIL_P(this_feature_index)) {
00200 st_insert(features_index, (st_data_t)ruby_strdup(short_feature_cstr), (st_data_t)offset);
00201 }
00202 else if (RB_TYPE_P(this_feature_index, T_FIXNUM)) {
00203 VALUE feature_indexes[2];
00204 feature_indexes[0] = this_feature_index;
00205 feature_indexes[1] = offset;
00206 this_feature_index = (VALUE)xcalloc(1, sizeof(struct RArray));
00207 RBASIC(this_feature_index)->flags = T_ARRAY;
00208 rb_ary_cat(this_feature_index, feature_indexes, numberof(feature_indexes));
00209 st_insert(features_index, (st_data_t)short_feature_cstr, (st_data_t)this_feature_index);
00210 }
00211 else {
00212 Check_Type(this_feature_index, T_ARRAY);
00213 rb_ary_push(this_feature_index, offset);
00214 }
00215 }
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225 static void
00226 features_index_add(VALUE feature, VALUE offset)
00227 {
00228 VALUE short_feature;
00229 const char *feature_str, *feature_end, *ext, *p;
00230
00231 feature_str = StringValuePtr(feature);
00232 feature_end = feature_str + RSTRING_LEN(feature);
00233
00234 for (ext = feature_end; ext > feature_str; ext--)
00235 if (*ext == '.' || *ext == '/')
00236 break;
00237 if (*ext != '.')
00238 ext = NULL;
00239
00240
00241
00242 p = ext ? ext : feature_end;
00243 while (1) {
00244 p--;
00245 while (p >= feature_str && *p != '/')
00246 p--;
00247 if (p < feature_str)
00248 break;
00249
00250 short_feature = rb_str_subseq(feature, p + 1 - feature_str, feature_end - p - 1);
00251 features_index_add_single(short_feature, offset);
00252 if (ext) {
00253 short_feature = rb_str_subseq(feature, p + 1 - feature_str, ext - p - 1);
00254 features_index_add_single(short_feature, offset);
00255 }
00256 }
00257 features_index_add_single(feature, offset);
00258 if (ext) {
00259 short_feature = rb_str_subseq(feature, 0, ext - feature_str);
00260 features_index_add_single(short_feature, offset);
00261 }
00262 }
00263
00264 static int
00265 loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
00266 {
00267 VALUE obj = (VALUE)val;
00268 if (!SPECIAL_CONST_P(obj)) {
00269 rb_ary_free(obj);
00270 xfree((void *)obj);
00271 }
00272 xfree((char *)key);
00273 return ST_DELETE;
00274 }
00275
00276 static st_table *
00277 get_loaded_features_index(void)
00278 {
00279 VALUE features;
00280 int i;
00281 rb_vm_t *vm = GET_VM();
00282
00283 if (!rb_ary_shared_with_p(vm->loaded_features_snapshot, vm->loaded_features)) {
00284
00285
00286 st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
00287 features = vm->loaded_features;
00288 for (i = 0; i < RARRAY_LEN(features); i++) {
00289 VALUE entry, as_str;
00290 as_str = entry = rb_ary_entry(features, i);
00291 StringValue(as_str);
00292 if (as_str != entry)
00293 rb_ary_store(features, i, as_str);
00294 rb_str_freeze(as_str);
00295 features_index_add(as_str, INT2FIX(i));
00296 }
00297 reset_loaded_features_snapshot();
00298 }
00299 return vm->loaded_features_index;
00300 }
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313 static VALUE
00314 loaded_feature_path(const char *name, long vlen, const char *feature, long len,
00315 int type, VALUE load_path)
00316 {
00317 long i;
00318 long plen;
00319 const char *e;
00320
00321 if (vlen < len+1) return 0;
00322 if (strchr(feature, '.') && !strncmp(name+(vlen-len), feature, len)) {
00323 plen = vlen - len;
00324 }
00325 else {
00326 for (e = name + vlen; name != e && *e != '.' && *e != '/'; --e);
00327 if (*e != '.' ||
00328 e-name < len ||
00329 strncmp(e-len, feature, len))
00330 return 0;
00331 plen = e - name - len;
00332 }
00333 if (plen > 0 && name[plen-1] != '/') {
00334 return 0;
00335 }
00336 if (type == 's' ? !IS_DLEXT(&name[plen+len]) :
00337 type == 'r' ? !IS_RBEXT(&name[plen+len]) :
00338 0) {
00339 return 0;
00340 }
00341
00342
00343
00344 if (plen > 0) --plen;
00345 for (i = 0; i < RARRAY_LEN(load_path); ++i) {
00346 VALUE p = RARRAY_AREF(load_path, i);
00347 const char *s = StringValuePtr(p);
00348 long n = RSTRING_LEN(p);
00349
00350 if (n != plen) continue;
00351 if (n && strncmp(name, s, n)) continue;
00352 return p;
00353 }
00354 return 0;
00355 }
00356
00357 struct loaded_feature_searching {
00358 const char *name;
00359 long len;
00360 int type;
00361 VALUE load_path;
00362 const char *result;
00363 };
00364
00365 static int
00366 loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
00367 {
00368 const char *s = (const char *)v;
00369 struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f;
00370 VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len,
00371 fp->type, fp->load_path);
00372 if (!p) return ST_CONTINUE;
00373 fp->result = s;
00374 return ST_STOP;
00375 }
00376
00377 static int
00378 rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn)
00379 {
00380 VALUE features, this_feature_index = Qnil, v, p, load_path = 0;
00381 const char *f, *e;
00382 long i, len, elen, n;
00383 st_table *loading_tbl, *features_index;
00384 st_data_t data;
00385 int type;
00386
00387 if (fn) *fn = 0;
00388 if (ext) {
00389 elen = strlen(ext);
00390 len = strlen(feature) - elen;
00391 type = rb ? 'r' : 's';
00392 }
00393 else {
00394 len = strlen(feature);
00395 elen = 0;
00396 type = 0;
00397 }
00398 features = get_loaded_features();
00399 features_index = get_loaded_features_index();
00400
00401 st_lookup(features_index, (st_data_t)feature, (st_data_t *)&this_feature_index);
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428 if (!NIL_P(this_feature_index)) {
00429 for (i = 0; ; i++) {
00430 VALUE entry;
00431 long index;
00432 if (RB_TYPE_P(this_feature_index, T_ARRAY)) {
00433 if (i >= RARRAY_LEN(this_feature_index)) break;
00434 entry = RARRAY_AREF(this_feature_index, i);
00435 }
00436 else {
00437 if (i > 0) break;
00438 entry = this_feature_index;
00439 }
00440 index = FIX2LONG(entry);
00441
00442 v = RARRAY_AREF(features, index);
00443 f = StringValuePtr(v);
00444 if ((n = RSTRING_LEN(v)) < len) continue;
00445 if (strncmp(f, feature, len) != 0) {
00446 if (expanded) continue;
00447 if (!load_path) load_path = rb_get_expanded_load_path();
00448 if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
00449 continue;
00450 expanded = 1;
00451 f += RSTRING_LEN(p) + 1;
00452 }
00453 if (!*(e = f + len)) {
00454 if (ext) continue;
00455 return 'u';
00456 }
00457 if (*e != '.') continue;
00458 if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
00459 return 's';
00460 }
00461 if ((rb || !ext) && (IS_RBEXT(e))) {
00462 return 'r';
00463 }
00464 }
00465 }
00466
00467 loading_tbl = get_loading_table();
00468 if (loading_tbl) {
00469 f = 0;
00470 if (!expanded) {
00471 struct loaded_feature_searching fs;
00472 fs.name = feature;
00473 fs.len = len;
00474 fs.type = type;
00475 fs.load_path = load_path ? load_path : rb_get_expanded_load_path();
00476 fs.result = 0;
00477 st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
00478 if ((f = fs.result) != 0) {
00479 if (fn) *fn = f;
00480 goto loading;
00481 }
00482 }
00483 if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
00484 if (fn) *fn = (const char*)data;
00485 loading:
00486 if (!ext) return 'u';
00487 return !IS_RBEXT(ext) ? 's' : 'r';
00488 }
00489 else {
00490 VALUE bufstr;
00491 char *buf;
00492 static const char so_ext[][4] = {
00493 ".so", ".o",
00494 };
00495
00496 if (ext && *ext) return 0;
00497 bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
00498 buf = RSTRING_PTR(bufstr);
00499 MEMCPY(buf, feature, char, len);
00500 for (i = 0; (e = loadable_ext[i]) != 0; i++) {
00501 strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
00502 if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
00503 rb_str_resize(bufstr, 0);
00504 if (fn) *fn = (const char*)data;
00505 return i ? 's' : 'r';
00506 }
00507 }
00508 for (i = 0; i < numberof(so_ext); i++) {
00509 strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1);
00510 if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
00511 rb_str_resize(bufstr, 0);
00512 if (fn) *fn = (const char*)data;
00513 return 's';
00514 }
00515 }
00516 rb_str_resize(bufstr, 0);
00517 }
00518 }
00519 return 0;
00520 }
00521
00522 int
00523 rb_provided(const char *feature)
00524 {
00525 return rb_feature_provided(feature, 0);
00526 }
00527
00528 int
00529 rb_feature_provided(const char *feature, const char **loading)
00530 {
00531 const char *ext = strrchr(feature, '.');
00532 volatile VALUE fullpath = 0;
00533
00534 if (*feature == '.' &&
00535 (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
00536 fullpath = rb_file_expand_path_fast(rb_get_path(rb_str_new2(feature)), Qnil);
00537 feature = RSTRING_PTR(fullpath);
00538 }
00539 if (ext && !strchr(ext, '/')) {
00540 if (IS_RBEXT(ext)) {
00541 if (rb_feature_p(feature, ext, TRUE, FALSE, loading)) return TRUE;
00542 return FALSE;
00543 }
00544 else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
00545 if (rb_feature_p(feature, ext, FALSE, FALSE, loading)) return TRUE;
00546 return FALSE;
00547 }
00548 }
00549 if (rb_feature_p(feature, 0, TRUE, FALSE, loading))
00550 return TRUE;
00551 return FALSE;
00552 }
00553
00554 static void
00555 rb_provide_feature(VALUE feature)
00556 {
00557 VALUE features;
00558
00559 features = get_loaded_features();
00560 if (OBJ_FROZEN(features)) {
00561 rb_raise(rb_eRuntimeError,
00562 "$LOADED_FEATURES is frozen; cannot append feature");
00563 }
00564 rb_str_freeze(feature);
00565
00566 rb_ary_push(features, feature);
00567 features_index_add(feature, INT2FIX(RARRAY_LEN(features)-1));
00568 reset_loaded_features_snapshot();
00569 }
00570
00571 void
00572 rb_provide(const char *feature)
00573 {
00574 rb_provide_feature(rb_usascii_str_new2(feature));
00575 }
00576
00577 NORETURN(static void load_failed(VALUE));
00578
00579 static inline void
00580 rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
00581 {
00582 int state;
00583 volatile VALUE wrapper = th->top_wrapper;
00584 volatile VALUE self = th->top_self;
00585 volatile int loaded = FALSE;
00586 volatile int mild_compile_error;
00587 #if !defined __GNUC__
00588 rb_thread_t *volatile th0 = th;
00589 #endif
00590
00591 th->errinfo = Qnil;
00592
00593 if (!wrap) {
00594 th->top_wrapper = 0;
00595 }
00596 else {
00597
00598 th->top_self = rb_obj_clone(rb_vm_top_self());
00599 th->top_wrapper = rb_module_new();
00600 rb_extend_object(th->top_self, th->top_wrapper);
00601 }
00602
00603 mild_compile_error = th->mild_compile_error;
00604 PUSH_TAG();
00605 state = EXEC_TAG();
00606 if (state == 0) {
00607 NODE *node;
00608 VALUE iseq;
00609
00610 th->mild_compile_error++;
00611 node = (NODE *)rb_load_file_str(fname);
00612 loaded = TRUE;
00613 iseq = rb_iseq_new_top(node, rb_str_new2("<top (required)>"), fname, rb_realpath_internal(Qnil, fname, 1), Qfalse);
00614 th->mild_compile_error--;
00615 rb_iseq_eval(iseq);
00616 }
00617 POP_TAG();
00618
00619 #if !defined __GNUC__
00620 th = th0;
00621 fname = RB_GC_GUARD(fname);
00622 #endif
00623 th->mild_compile_error = mild_compile_error;
00624 th->top_self = self;
00625 th->top_wrapper = wrapper;
00626
00627 if (!loaded && !FIXNUM_P(th->errinfo)) {
00628
00629 rb_exc_raise(th->errinfo);
00630 }
00631 if (state) {
00632 rb_vm_jump_tag_but_local_jump(state);
00633 }
00634
00635 if (!NIL_P(th->errinfo)) {
00636
00637 rb_exc_raise(th->errinfo);
00638 }
00639 }
00640
00641 static void
00642 rb_load_internal(VALUE fname, int wrap)
00643 {
00644 rb_load_internal0(GET_THREAD(), fname, wrap);
00645 }
00646
00647 void
00648 rb_load(VALUE fname, int wrap)
00649 {
00650 VALUE tmp = rb_find_file(FilePathValue(fname));
00651 if (!tmp) load_failed(fname);
00652 rb_load_internal(tmp, wrap);
00653 }
00654
00655 void
00656 rb_load_protect(VALUE fname, int wrap, int *state)
00657 {
00658 int status;
00659
00660 PUSH_TAG();
00661 if ((status = EXEC_TAG()) == 0) {
00662 rb_load(fname, wrap);
00663 }
00664 POP_TAG();
00665 if (state)
00666 *state = status;
00667 }
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683 static VALUE
00684 rb_f_load(int argc, VALUE *argv)
00685 {
00686 VALUE fname, wrap, path, orig_fname;
00687
00688 rb_scan_args(argc, argv, "11", &fname, &wrap);
00689
00690 if (RUBY_DTRACE_LOAD_ENTRY_ENABLED()) {
00691 RUBY_DTRACE_LOAD_ENTRY(StringValuePtr(fname),
00692 rb_sourcefile(),
00693 rb_sourceline());
00694 }
00695
00696 orig_fname = FilePathValue(fname);
00697 fname = rb_str_encode_ospath(orig_fname);
00698 path = rb_find_file(fname);
00699 if (!path) {
00700 if (!rb_file_load_ok(RSTRING_PTR(fname)))
00701 load_failed(orig_fname);
00702 path = fname;
00703 }
00704 rb_load_internal(path, RTEST(wrap));
00705
00706 if (RUBY_DTRACE_LOAD_RETURN_ENABLED()) {
00707 RUBY_DTRACE_LOAD_RETURN(StringValuePtr(fname),
00708 rb_sourcefile(),
00709 rb_sourceline());
00710 }
00711
00712 return Qtrue;
00713 }
00714
00715 static char *
00716 load_lock(const char *ftptr)
00717 {
00718 st_data_t data;
00719 st_table *loading_tbl = get_loading_table();
00720
00721 if (!loading_tbl || !st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
00722
00723 if (!loading_tbl) {
00724 GET_VM()->loading_table = loading_tbl = st_init_strtable();
00725 }
00726
00727 ftptr = ruby_strdup(ftptr);
00728 data = (st_data_t)rb_thread_shield_new();
00729 st_insert(loading_tbl, (st_data_t)ftptr, data);
00730 return (char *)ftptr;
00731 }
00732 else if (RB_TYPE_P((VALUE)data, T_NODE) && nd_type((VALUE)data) == NODE_MEMO) {
00733 NODE *memo = RNODE(data);
00734 void (*init)(void) = (void (*)(void))memo->nd_cfnc;
00735 data = (st_data_t)rb_thread_shield_new();
00736 st_insert(loading_tbl, (st_data_t)ftptr, data);
00737 (*init)();
00738 return (char *)"";
00739 }
00740 if (RTEST(ruby_verbose)) {
00741 rb_warning("loading in progress, circular require considered harmful - %s", ftptr);
00742 rb_backtrace_print_to(rb_stderr);
00743 }
00744 switch (rb_thread_shield_wait((VALUE)data)) {
00745 case Qfalse:
00746 data = (st_data_t)ftptr;
00747 st_insert(loading_tbl, data, (st_data_t)rb_thread_shield_new());
00748 return 0;
00749 case Qnil:
00750 return 0;
00751 }
00752 return (char *)ftptr;
00753 }
00754
00755 static int
00756 release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int existing)
00757 {
00758 VALUE thread_shield = (VALUE)*value;
00759 if (!existing) return ST_STOP;
00760 if (done ? rb_thread_shield_destroy(thread_shield) : rb_thread_shield_release(thread_shield)) {
00761
00762 return ST_CONTINUE;
00763 }
00764 xfree((char *)*key);
00765 return ST_DELETE;
00766 }
00767
00768 static void
00769 load_unlock(const char *ftptr, int done)
00770 {
00771 if (ftptr) {
00772 st_data_t key = (st_data_t)ftptr;
00773 st_table *loading_tbl = get_loading_table();
00774
00775 st_update(loading_tbl, key, release_thread_shield, done);
00776 }
00777 }
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816 VALUE
00817 rb_f_require(VALUE obj, VALUE fname)
00818 {
00819 return rb_require_safe(fname, rb_safe_level());
00820 }
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830 VALUE
00831 rb_f_require_relative(VALUE obj, VALUE fname)
00832 {
00833 VALUE base = rb_current_realfilepath();
00834 if (NIL_P(base)) {
00835 rb_loaderror("cannot infer basepath");
00836 }
00837 base = rb_file_dirname(base);
00838 return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level());
00839 }
00840
00841 static int
00842 search_required(VALUE fname, volatile VALUE *path, int safe_level)
00843 {
00844 VALUE tmp;
00845 char *ext, *ftptr;
00846 int type, ft = 0;
00847 const char *loading;
00848
00849 *path = 0;
00850 ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
00851 if (ext && !strchr(ext, '/')) {
00852 if (IS_RBEXT(ext)) {
00853 if (rb_feature_p(ftptr, ext, TRUE, FALSE, &loading)) {
00854 if (loading) *path = rb_filesystem_str_new_cstr(loading);
00855 return 'r';
00856 }
00857 if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
00858 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00859 if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading)
00860 *path = tmp;
00861 return 'r';
00862 }
00863 return 0;
00864 }
00865 else if (IS_SOEXT(ext)) {
00866 if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
00867 if (loading) *path = rb_filesystem_str_new_cstr(loading);
00868 return 's';
00869 }
00870 tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname));
00871 #ifdef DLEXT2
00872 OBJ_FREEZE(tmp);
00873 if (rb_find_file_ext_safe(&tmp, loadable_ext + 1, safe_level)) {
00874 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00875 if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
00876 *path = tmp;
00877 return 's';
00878 }
00879 #else
00880 rb_str_cat2(tmp, DLEXT);
00881 OBJ_FREEZE(tmp);
00882 if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) {
00883 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00884 if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
00885 *path = tmp;
00886 return 's';
00887 }
00888 #endif
00889 }
00890 else if (IS_DLEXT(ext)) {
00891 if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
00892 if (loading) *path = rb_filesystem_str_new_cstr(loading);
00893 return 's';
00894 }
00895 if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
00896 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00897 if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
00898 *path = tmp;
00899 return 's';
00900 }
00901 }
00902 }
00903 else if ((ft = rb_feature_p(ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
00904 if (loading) *path = rb_filesystem_str_new_cstr(loading);
00905 return 'r';
00906 }
00907 tmp = fname;
00908 type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level);
00909 switch (type) {
00910 case 0:
00911 if (ft)
00912 goto statically_linked;
00913 ftptr = RSTRING_PTR(tmp);
00914 return rb_feature_p(ftptr, 0, FALSE, TRUE, 0);
00915
00916 default:
00917 if (ft) {
00918 statically_linked:
00919 if (loading) *path = rb_filesystem_str_new_cstr(loading);
00920 return ft;
00921 }
00922 case 1:
00923 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00924 if (rb_feature_p(ftptr, ext, !--type, TRUE, &loading) && !loading)
00925 break;
00926 *path = tmp;
00927 }
00928 return type ? 's' : 'r';
00929 }
00930
00931 static void
00932 load_failed(VALUE fname)
00933 {
00934 rb_load_fail(fname, "cannot load such file");
00935 }
00936
00937 static VALUE
00938 load_ext(VALUE path)
00939 {
00940 SCOPE_SET(NOEX_PUBLIC);
00941 return (VALUE)dln_load(RSTRING_PTR(path));
00942 }
00943
00944 VALUE
00945 rb_require_safe(VALUE fname, int safe)
00946 {
00947 volatile VALUE result = Qnil;
00948 rb_thread_t *th = GET_THREAD();
00949 volatile VALUE errinfo = th->errinfo;
00950 int state;
00951 struct {
00952 int safe;
00953 } volatile saved;
00954 char *volatile ftptr = 0;
00955
00956 if (RUBY_DTRACE_REQUIRE_ENTRY_ENABLED()) {
00957 RUBY_DTRACE_REQUIRE_ENTRY(StringValuePtr(fname),
00958 rb_sourcefile(),
00959 rb_sourceline());
00960 }
00961
00962 PUSH_TAG();
00963 saved.safe = rb_safe_level();
00964 if ((state = EXEC_TAG()) == 0) {
00965 VALUE path;
00966 long handle;
00967 int found;
00968
00969 rb_set_safe_level_force(safe);
00970 FilePathValue(fname);
00971 rb_set_safe_level_force(0);
00972
00973 if (RUBY_DTRACE_FIND_REQUIRE_ENTRY_ENABLED()) {
00974 RUBY_DTRACE_FIND_REQUIRE_ENTRY(StringValuePtr(fname),
00975 rb_sourcefile(),
00976 rb_sourceline());
00977 }
00978
00979 path = rb_str_encode_ospath(fname);
00980 found = search_required(path, &path, safe);
00981
00982 if (RUBY_DTRACE_FIND_REQUIRE_RETURN_ENABLED()) {
00983 RUBY_DTRACE_FIND_REQUIRE_RETURN(StringValuePtr(fname),
00984 rb_sourcefile(),
00985 rb_sourceline());
00986 }
00987 if (found) {
00988 if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
00989 result = Qfalse;
00990 }
00991 else if (!*ftptr) {
00992 rb_provide_feature(path);
00993 result = Qtrue;
00994 }
00995 else {
00996 switch (found) {
00997 case 'r':
00998 rb_load_internal(path, 0);
00999 break;
01000
01001 case 's':
01002 handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
01003 path, 0, path);
01004 rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
01005 break;
01006 }
01007 rb_provide_feature(path);
01008 result = Qtrue;
01009 }
01010 }
01011 }
01012 POP_TAG();
01013 load_unlock(ftptr, !state);
01014
01015 rb_set_safe_level_force(saved.safe);
01016 if (state) {
01017 JUMP_TAG(state);
01018 }
01019
01020 if (NIL_P(result)) {
01021 load_failed(fname);
01022 }
01023
01024 th->errinfo = errinfo;
01025
01026 if (RUBY_DTRACE_REQUIRE_RETURN_ENABLED()) {
01027 RUBY_DTRACE_REQUIRE_RETURN(StringValuePtr(fname),
01028 rb_sourcefile(),
01029 rb_sourceline());
01030 }
01031
01032 return result;
01033 }
01034
01035 VALUE
01036 rb_require(const char *fname)
01037 {
01038 VALUE fn = rb_str_new2(fname);
01039 OBJ_FREEZE(fn);
01040 return rb_require_safe(fn, rb_safe_level());
01041 }
01042
01043 static int
01044 register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing)
01045 {
01046 const char *name = (char *)*key;
01047 if (existing) {
01048
01049 rb_warn("%s is already registered", name);
01050 }
01051 else {
01052 *value = (st_data_t)NEW_MEMO(init, 0, 0);
01053 *key = (st_data_t)ruby_strdup(name);
01054 }
01055 return ST_CONTINUE;
01056 }
01057
01058 RUBY_FUNC_EXPORTED void
01059 ruby_init_ext(const char *name, void (*init)(void))
01060 {
01061 st_table *loading_tbl = get_loading_table();
01062
01063 if (!loading_tbl) {
01064 GET_VM()->loading_table = loading_tbl = st_init_strtable();
01065 }
01066 st_update(loading_tbl, (st_data_t)name, register_init_ext, (st_data_t)init);
01067 }
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083 static VALUE
01084 rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
01085 {
01086 ID id = rb_to_id(sym);
01087
01088 FilePathValue(file);
01089 rb_autoload(mod, id, RSTRING_PTR(file));
01090 return Qnil;
01091 }
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106 static VALUE
01107 rb_mod_autoload_p(VALUE mod, VALUE sym)
01108 {
01109 ID id = rb_check_id(&sym);
01110 if (!id) {
01111 return Qnil;
01112 }
01113 return rb_autoload_p(mod, id);
01114 }
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127 static VALUE
01128 rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
01129 {
01130 VALUE klass = rb_class_real(rb_vm_cbase());
01131 if (NIL_P(klass)) {
01132 rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
01133 }
01134 return rb_mod_autoload(klass, sym, file);
01135 }
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148 static VALUE
01149 rb_f_autoload_p(VALUE obj, VALUE sym)
01150 {
01151
01152 VALUE klass = rb_vm_cbase();
01153 if (NIL_P(klass)) {
01154 return Qnil;
01155 }
01156 return rb_mod_autoload_p(klass, sym);
01157 }
01158
01159 void
01160 Init_load()
01161 {
01162 #undef rb_intern
01163 #define rb_intern(str) rb_intern2((str), strlen(str))
01164 rb_vm_t *vm = GET_VM();
01165 static const char var_load_path[] = "$:";
01166 ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1);
01167
01168 rb_define_hooked_variable(var_load_path, (VALUE*)vm, load_path_getter, rb_gvar_readonly_setter);
01169 rb_alias_variable(rb_intern("$-I"), id_load_path);
01170 rb_alias_variable(rb_intern("$LOAD_PATH"), id_load_path);
01171 vm->load_path = rb_ary_new();
01172 vm->expanded_load_path = rb_ary_tmp_new(0);
01173 vm->load_path_snapshot = rb_ary_tmp_new(0);
01174 vm->load_path_check_cache = 0;
01175
01176 rb_define_virtual_variable("$\"", get_loaded_features, 0);
01177 rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
01178 vm->loaded_features = rb_ary_new();
01179 vm->loaded_features_snapshot = rb_ary_tmp_new(0);
01180 vm->loaded_features_index = st_init_strtable();
01181
01182 rb_define_global_function("load", rb_f_load, -1);
01183 rb_define_global_function("require", rb_f_require, 1);
01184 rb_define_global_function("require_relative", rb_f_require_relative, 1);
01185 rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
01186 rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
01187 rb_define_global_function("autoload", rb_f_autoload, 2);
01188 rb_define_global_function("autoload?", rb_f_autoload_p, 1);
01189
01190 ruby_dln_librefs = rb_ary_tmp_new(0);
01191 rb_gc_register_mark_object(ruby_dln_librefs);
01192 }
01193