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