00001
00002
00003
00004
00005 #include "ruby/ruby.h"
00006 #include "ruby/util.h"
00007 #include "dln.h"
00008 #include "eval_intern.h"
00009
00010 VALUE ruby_dln_librefs;
00011
00012 #define IS_RBEXT(e) (strcmp(e, ".rb") == 0)
00013 #define IS_SOEXT(e) (strcmp(e, ".so") == 0 || strcmp(e, ".o") == 0)
00014 #ifdef DLEXT2
00015 #define IS_DLEXT(e) (strcmp(e, DLEXT) == 0 || strcmp(e, DLEXT2) == 0)
00016 #else
00017 #define IS_DLEXT(e) (strcmp(e, DLEXT) == 0)
00018 #endif
00019
00020
00021 static const char *const loadable_ext[] = {
00022 ".rb", DLEXT,
00023 #ifdef DLEXT2
00024 DLEXT2,
00025 #endif
00026 0
00027 };
00028
00029 VALUE
00030 rb_get_load_path(void)
00031 {
00032 VALUE load_path = GET_VM()->load_path;
00033 return load_path;
00034 }
00035
00036 VALUE
00037 rb_get_expanded_load_path(void)
00038 {
00039 VALUE load_path = rb_get_load_path();
00040 VALUE ary;
00041 long i;
00042
00043 ary = rb_ary_new2(RARRAY_LEN(load_path));
00044 for (i = 0; i < RARRAY_LEN(load_path); ++i) {
00045 VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
00046 rb_str_freeze(path);
00047 rb_ary_push(ary, path);
00048 }
00049 rb_obj_freeze(ary);
00050 return ary;
00051 }
00052
00053 static VALUE
00054 load_path_getter(ID id, rb_vm_t *vm)
00055 {
00056 return vm->load_path;
00057 }
00058
00059 static VALUE
00060 get_loaded_features(void)
00061 {
00062 return GET_VM()->loaded_features;
00063 }
00064
00065 static st_table *
00066 get_loading_table(void)
00067 {
00068 return GET_VM()->loading_table;
00069 }
00070
00071 static VALUE
00072 loaded_feature_path(const char *name, long vlen, const char *feature, long len,
00073 int type, VALUE load_path)
00074 {
00075 long i;
00076
00077 for (i = 0; i < RARRAY_LEN(load_path); ++i) {
00078 VALUE p = RARRAY_PTR(load_path)[i];
00079 const char *s = StringValuePtr(p);
00080 long n = RSTRING_LEN(p);
00081
00082 if (vlen < n + len + 1) continue;
00083 if (n && (strncmp(name, s, n) || name[n] != '/')) continue;
00084 if (strncmp(name + n + 1, feature, len)) continue;
00085 if (name[n+len+1] && name[n+len+1] != '.') continue;
00086 switch (type) {
00087 case 's':
00088 if (IS_DLEXT(&name[n+len+1])) return p;
00089 break;
00090 case 'r':
00091 if (IS_RBEXT(&name[n+len+1])) return p;
00092 break;
00093 default:
00094 return p;
00095 }
00096 }
00097 return 0;
00098 }
00099
00100 struct loaded_feature_searching {
00101 const char *name;
00102 long len;
00103 int type;
00104 VALUE load_path;
00105 const char *result;
00106 };
00107
00108 static int
00109 loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
00110 {
00111 const char *s = (const char *)v;
00112 struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f;
00113 VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len,
00114 fp->type, fp->load_path);
00115 if (!p) return ST_CONTINUE;
00116 fp->result = s;
00117 return ST_STOP;
00118 }
00119
00120 static int
00121 rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn)
00122 {
00123 VALUE v, features, p, load_path = 0;
00124 const char *f, *e;
00125 long i, len, elen, n;
00126 st_table *loading_tbl;
00127 st_data_t data;
00128 int type;
00129
00130 if (fn) *fn = 0;
00131 if (ext) {
00132 elen = strlen(ext);
00133 len = strlen(feature) - elen;
00134 type = rb ? 'r' : 's';
00135 }
00136 else {
00137 len = strlen(feature);
00138 elen = 0;
00139 type = 0;
00140 }
00141 features = get_loaded_features();
00142 for (i = 0; i < RARRAY_LEN(features); ++i) {
00143 v = RARRAY_PTR(features)[i];
00144 f = StringValuePtr(v);
00145 if ((n = RSTRING_LEN(v)) < len) continue;
00146 if (strncmp(f, feature, len) != 0) {
00147 if (expanded) continue;
00148 if (!load_path) load_path = rb_get_expanded_load_path();
00149 if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
00150 continue;
00151 expanded = 1;
00152 f += RSTRING_LEN(p) + 1;
00153 }
00154 if (!*(e = f + len)) {
00155 if (ext) continue;
00156 return 'u';
00157 }
00158 if (*e != '.') continue;
00159 if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
00160 return 's';
00161 }
00162 if ((rb || !ext) && (IS_RBEXT(e))) {
00163 return 'r';
00164 }
00165 }
00166 loading_tbl = get_loading_table();
00167 if (loading_tbl) {
00168 f = 0;
00169 if (!expanded) {
00170 struct loaded_feature_searching fs;
00171 fs.name = feature;
00172 fs.len = len;
00173 fs.type = type;
00174 fs.load_path = load_path ? load_path : rb_get_load_path();
00175 fs.result = 0;
00176 st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
00177 if ((f = fs.result) != 0) {
00178 if (fn) *fn = f;
00179 goto loading;
00180 }
00181 }
00182 if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
00183 if (fn) *fn = (const char*)data;
00184 loading:
00185 if (!ext) return 'u';
00186 return !IS_RBEXT(ext) ? 's' : 'r';
00187 }
00188 else {
00189 VALUE bufstr;
00190 char *buf;
00191
00192 if (ext && *ext) return 0;
00193 bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
00194 buf = RSTRING_PTR(bufstr);
00195 MEMCPY(buf, feature, char, len);
00196 for (i = 0; (e = loadable_ext[i]) != 0; i++) {
00197 strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
00198 if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
00199 rb_str_resize(bufstr, 0);
00200 if (fn) *fn = (const char*)data;
00201 return i ? 's' : 'r';
00202 }
00203 }
00204 rb_str_resize(bufstr, 0);
00205 }
00206 }
00207 return 0;
00208 }
00209
00210 int
00211 rb_provided(const char *feature)
00212 {
00213 return rb_feature_provided(feature, 0);
00214 }
00215
00216 int
00217 rb_feature_provided(const char *feature, const char **loading)
00218 {
00219 const char *ext = strrchr(feature, '.');
00220 volatile VALUE fullpath = 0;
00221
00222 if (*feature == '.' &&
00223 (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
00224 fullpath = rb_file_expand_path(rb_str_new2(feature), Qnil);
00225 feature = RSTRING_PTR(fullpath);
00226 }
00227 if (ext && !strchr(ext, '/')) {
00228 if (IS_RBEXT(ext)) {
00229 if (rb_feature_p(feature, ext, TRUE, FALSE, loading)) return TRUE;
00230 return FALSE;
00231 }
00232 else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
00233 if (rb_feature_p(feature, ext, FALSE, FALSE, loading)) return TRUE;
00234 return FALSE;
00235 }
00236 }
00237 if (rb_feature_p(feature, 0, TRUE, FALSE, loading))
00238 return TRUE;
00239 return FALSE;
00240 }
00241
00242 static void
00243 rb_provide_feature(VALUE feature)
00244 {
00245 rb_ary_push(get_loaded_features(), feature);
00246 }
00247
00248 void
00249 rb_provide(const char *feature)
00250 {
00251 rb_provide_feature(rb_usascii_str_new2(feature));
00252 }
00253
00254 NORETURN(static void load_failed(VALUE));
00255
00256 static void
00257 rb_load_internal(VALUE fname, int wrap)
00258 {
00259 int state;
00260 rb_thread_t *th = GET_THREAD();
00261 volatile VALUE wrapper = th->top_wrapper;
00262 volatile VALUE self = th->top_self;
00263 volatile int loaded = FALSE;
00264 volatile int mild_compile_error;
00265 #ifndef __GNUC__
00266 rb_thread_t *volatile th0 = th;
00267 #endif
00268
00269 th->errinfo = Qnil;
00270
00271 if (!wrap) {
00272 rb_secure(4);
00273 th->top_wrapper = 0;
00274 }
00275 else {
00276
00277 th->top_self = rb_obj_clone(rb_vm_top_self());
00278 th->top_wrapper = rb_module_new();
00279 rb_extend_object(th->top_self, th->top_wrapper);
00280 }
00281
00282 mild_compile_error = th->mild_compile_error;
00283 PUSH_TAG();
00284 state = EXEC_TAG();
00285 if (state == 0) {
00286 NODE *node;
00287 VALUE iseq;
00288
00289 th->mild_compile_error++;
00290 node = (NODE *)rb_load_file(RSTRING_PTR(fname));
00291 loaded = TRUE;
00292 iseq = rb_iseq_new_top(node, rb_str_new2("<top (required)>"), fname, fname, Qfalse);
00293 th->mild_compile_error--;
00294 rb_iseq_eval(iseq);
00295 }
00296 POP_TAG();
00297
00298 #ifndef __GNUC__
00299 th = th0;
00300 fname = RB_GC_GUARD(fname);
00301 #endif
00302 th->mild_compile_error = mild_compile_error;
00303 th->top_self = self;
00304 th->top_wrapper = wrapper;
00305
00306 if (!loaded) {
00307 rb_exc_raise(GET_THREAD()->errinfo);
00308 }
00309 if (state) {
00310 rb_vm_jump_tag_but_local_jump(state, Qundef);
00311 }
00312
00313 if (!NIL_P(GET_THREAD()->errinfo)) {
00314
00315 rb_exc_raise(th->errinfo);
00316 }
00317 }
00318
00319 void
00320 rb_load(VALUE fname, int wrap)
00321 {
00322 VALUE tmp = rb_find_file(FilePathValue(fname));
00323 if (!tmp) load_failed(fname);
00324 rb_load_internal(tmp, wrap);
00325 }
00326
00327 void
00328 rb_load_protect(VALUE fname, int wrap, int *state)
00329 {
00330 int status;
00331
00332 PUSH_TAG();
00333 if ((status = EXEC_TAG()) == 0) {
00334 rb_load(fname, wrap);
00335 }
00336 POP_TAG();
00337 if (state)
00338 *state = status;
00339 }
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355 static VALUE
00356 rb_f_load(int argc, VALUE *argv)
00357 {
00358 VALUE fname, wrap, path;
00359
00360 rb_scan_args(argc, argv, "11", &fname, &wrap);
00361 path = rb_find_file(FilePathValue(fname));
00362 if (!path) {
00363 if (!rb_file_load_ok(RSTRING_PTR(fname)))
00364 load_failed(fname);
00365 path = fname;
00366 }
00367 rb_load_internal(path, RTEST(wrap));
00368 return Qtrue;
00369 }
00370
00371 static char *
00372 load_lock(const char *ftptr)
00373 {
00374 st_data_t data;
00375 st_table *loading_tbl = get_loading_table();
00376
00377 if (!loading_tbl || !st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
00378
00379 if (!loading_tbl) {
00380 GET_VM()->loading_table = loading_tbl = st_init_strtable();
00381 }
00382
00383 ftptr = ruby_strdup(ftptr);
00384 data = (st_data_t)rb_barrier_new();
00385 st_insert(loading_tbl, (st_data_t)ftptr, data);
00386 return (char *)ftptr;
00387 }
00388 if (RTEST(ruby_verbose)) {
00389 rb_warning("loading in progress, circular require considered harmful - %s", ftptr);
00390 rb_backtrace();
00391 }
00392 return RTEST(rb_barrier_wait((VALUE)data)) ? (char *)ftptr : 0;
00393 }
00394
00395 static void
00396 load_unlock(const char *ftptr, int done)
00397 {
00398 if (ftptr) {
00399 st_data_t key = (st_data_t)ftptr;
00400 st_data_t data;
00401 st_table *loading_tbl = get_loading_table();
00402
00403 if (st_delete(loading_tbl, &key, &data)) {
00404 VALUE barrier = (VALUE)data;
00405 xfree((char *)key);
00406 if (done)
00407 rb_barrier_destroy(barrier);
00408 else
00409 rb_barrier_release(barrier);
00410 }
00411 }
00412 }
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437 VALUE
00438 rb_f_require(VALUE obj, VALUE fname)
00439 {
00440 return rb_require_safe(fname, rb_safe_level());
00441 }
00442
00443 VALUE
00444 rb_f_require_relative(VALUE obj, VALUE fname)
00445 {
00446 VALUE rb_current_realfilepath(void);
00447 VALUE base = rb_current_realfilepath();
00448 if (NIL_P(base)) {
00449 rb_raise(rb_eLoadError, "cannot infer basepath");
00450 }
00451 base = rb_file_dirname(base);
00452 return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level());
00453 }
00454
00455 static int
00456 search_required(VALUE fname, volatile VALUE *path, int safe_level)
00457 {
00458 VALUE tmp;
00459 char *ext, *ftptr;
00460 int type, ft = 0;
00461 const char *loading;
00462
00463 *path = 0;
00464 ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
00465 if (ext && !strchr(ext, '/')) {
00466 if (IS_RBEXT(ext)) {
00467 if (rb_feature_p(ftptr, ext, TRUE, FALSE, &loading)) {
00468 if (loading) *path = rb_str_new2(loading);
00469 return 'r';
00470 }
00471 if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
00472 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00473 if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading)
00474 *path = tmp;
00475 return 'r';
00476 }
00477 return 0;
00478 }
00479 else if (IS_SOEXT(ext)) {
00480 if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
00481 if (loading) *path = rb_str_new2(loading);
00482 return 's';
00483 }
00484 tmp = rb_str_new(RSTRING_PTR(fname), ext - RSTRING_PTR(fname));
00485 #ifdef DLEXT2
00486 OBJ_FREEZE(tmp);
00487 if (rb_find_file_ext_safe(&tmp, loadable_ext + 1, safe_level)) {
00488 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00489 if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
00490 *path = tmp;
00491 return 's';
00492 }
00493 #else
00494 rb_str_cat2(tmp, DLEXT);
00495 OBJ_FREEZE(tmp);
00496 if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) {
00497 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00498 if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
00499 *path = tmp;
00500 return 's';
00501 }
00502 #endif
00503 }
00504 else if (IS_DLEXT(ext)) {
00505 if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
00506 if (loading) *path = rb_str_new2(loading);
00507 return 's';
00508 }
00509 if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
00510 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00511 if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
00512 *path = tmp;
00513 return 's';
00514 }
00515 }
00516 }
00517 else if ((ft = rb_feature_p(ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
00518 if (loading) *path = rb_str_new2(loading);
00519 return 'r';
00520 }
00521 tmp = fname;
00522 type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level);
00523 switch (type) {
00524 case 0:
00525 if (ft)
00526 break;
00527 ftptr = RSTRING_PTR(tmp);
00528 return rb_feature_p(ftptr, 0, FALSE, TRUE, 0);
00529
00530 default:
00531 if (ft)
00532 break;
00533 case 1:
00534 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
00535 if (rb_feature_p(ftptr, ext, !--type, TRUE, &loading) && !loading)
00536 break;
00537 *path = tmp;
00538 }
00539 return type ? 's' : 'r';
00540 }
00541
00542 static void
00543 load_failed(VALUE fname)
00544 {
00545 VALUE mesg = rb_str_buf_new_cstr("no such file to load -- ");
00546 rb_str_append(mesg, fname);
00547 rb_exc_raise(rb_exc_new3(rb_eLoadError, mesg));
00548 }
00549
00550 static VALUE
00551 load_ext(VALUE path)
00552 {
00553 SCOPE_SET(NOEX_PUBLIC);
00554 return (VALUE)dln_load(RSTRING_PTR(path));
00555 }
00556
00557 VALUE
00558 rb_require_safe(VALUE fname, int safe)
00559 {
00560 volatile VALUE result = Qnil;
00561 rb_thread_t *th = GET_THREAD();
00562 volatile VALUE errinfo = th->errinfo;
00563 int state;
00564 struct {
00565 int safe;
00566 } volatile saved;
00567 char *volatile ftptr = 0;
00568
00569 PUSH_TAG();
00570 saved.safe = rb_safe_level();
00571 if ((state = EXEC_TAG()) == 0) {
00572 VALUE path;
00573 long handle;
00574 int found;
00575
00576 rb_set_safe_level_force(safe);
00577 FilePathValue(fname);
00578 rb_set_safe_level_force(0);
00579 found = search_required(fname, &path, safe);
00580 if (found) {
00581 if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
00582 result = Qfalse;
00583 }
00584 else {
00585 switch (found) {
00586 case 'r':
00587 rb_load_internal(path, 0);
00588 break;
00589
00590 case 's':
00591 handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
00592 path, 0, path, path);
00593 rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
00594 break;
00595 }
00596 rb_provide_feature(path);
00597 result = Qtrue;
00598 }
00599 }
00600 }
00601 POP_TAG();
00602 load_unlock(ftptr, !state);
00603
00604 rb_set_safe_level_force(saved.safe);
00605 if (state) {
00606 JUMP_TAG(state);
00607 }
00608
00609 if (NIL_P(result)) {
00610 load_failed(fname);
00611 }
00612
00613 th->errinfo = errinfo;
00614
00615 return result;
00616 }
00617
00618 VALUE
00619 rb_require(const char *fname)
00620 {
00621 VALUE fn = rb_str_new2(fname);
00622 OBJ_FREEZE(fn);
00623 return rb_require_safe(fn, rb_safe_level());
00624 }
00625
00626 static VALUE
00627 init_ext_call(VALUE arg)
00628 {
00629 SCOPE_SET(NOEX_PUBLIC);
00630 (*(void (*)(void))arg)();
00631 return Qnil;
00632 }
00633
00634 void
00635 ruby_init_ext(const char *name, void (*init)(void))
00636 {
00637 if (load_lock(name)) {
00638 rb_vm_call_cfunc(rb_vm_top_self(), init_ext_call, (VALUE)init,
00639 0, rb_str_new2(name), Qnil);
00640 rb_provide(name);
00641 load_unlock(name, 1);
00642 }
00643 }
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659 static VALUE
00660 rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
00661 {
00662 ID id = rb_to_id(sym);
00663
00664 FilePathValue(file);
00665 rb_autoload(mod, id, RSTRING_PTR(file));
00666 return Qnil;
00667 }
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682 static VALUE
00683 rb_mod_autoload_p(VALUE mod, VALUE sym)
00684 {
00685 return rb_autoload_p(mod, rb_to_id(sym));
00686 }
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699 static VALUE
00700 rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
00701 {
00702 VALUE klass = rb_vm_cbase();
00703 if (NIL_P(klass)) {
00704 rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
00705 }
00706 return rb_mod_autoload(klass, sym, file);
00707 }
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720 static VALUE
00721 rb_f_autoload_p(VALUE obj, VALUE sym)
00722 {
00723
00724 VALUE klass = rb_vm_cbase();
00725 if (NIL_P(klass)) {
00726 return Qnil;
00727 }
00728 return rb_mod_autoload_p(klass, sym);
00729 }
00730
00731 void
00732 Init_load()
00733 {
00734 #undef rb_intern
00735 #define rb_intern(str) rb_intern2(str, strlen(str))
00736 rb_vm_t *vm = GET_VM();
00737 static const char var_load_path[] = "$:";
00738 ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1);
00739
00740 rb_define_hooked_variable(var_load_path, (VALUE*)vm, load_path_getter, rb_gvar_readonly_setter);
00741 rb_alias_variable(rb_intern("$-I"), id_load_path);
00742 rb_alias_variable(rb_intern("$LOAD_PATH"), id_load_path);
00743 vm->load_path = rb_ary_new();
00744
00745 rb_define_virtual_variable("$\"", get_loaded_features, 0);
00746 rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
00747 vm->loaded_features = rb_ary_new();
00748
00749 rb_define_global_function("load", rb_f_load, -1);
00750 rb_define_global_function("require", rb_f_require, 1);
00751 rb_define_global_function("require_relative", rb_f_require_relative, 1);
00752 rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
00753 rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
00754 rb_define_global_function("autoload", rb_f_autoload, 2);
00755 rb_define_global_function("autoload?", rb_f_autoload_p, 1);
00756
00757 ruby_dln_librefs = rb_ary_new();
00758 rb_gc_register_mark_object(ruby_dln_librefs);
00759 }
00760