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