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