diff options
Diffstat (limited to 'load.c')
-rw-r--r-- | load.c | 202 |
1 files changed, 162 insertions, 40 deletions
@@ -8,8 +8,9 @@ #include "internal/dir.h" #include "internal/error.h" #include "internal/file.h" +#include "internal/hash.h" #include "internal/load.h" -#include "internal/parse.h" +#include "internal/ruby_parser.h" #include "internal/thread.h" #include "internal/variable.h" #include "iseq.h" @@ -18,12 +19,27 @@ #include "ruby/encoding.h" #include "ruby/util.h" -static VALUE ruby_dln_librefs; +static VALUE ruby_dln_libmap; #define IS_RBEXT(e) (strcmp((e), ".rb") == 0) #define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0) #define IS_DLEXT(e) (strcmp((e), DLEXT) == 0) +#if SIZEOF_VALUE <= SIZEOF_LONG +# define SVALUE2NUM(x) LONG2NUM((long)(x)) +# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LONG(x) +#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG +# define SVALUE2NUM(x) LL2NUM((LONG_LONG)(x)) +# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LL(x) +#else +# error Need integer for VALUE +#endif + +enum { + loadable_ext_rb = (0+ /* .rb extension is the first in both tables */ + 1) /* offset by rb_find_file_ext() */ +}; + static const char *const loadable_ext[] = { ".rb", DLEXT, 0 @@ -237,9 +253,9 @@ features_index_add_single_callback(st_data_t *key, st_data_t *value, st_data_t r rb_darray_set(feature_indexes, top^0, FIX2LONG(this_feature_index)); rb_darray_set(feature_indexes, top^1, FIX2LONG(offset)); - assert(rb_darray_size(feature_indexes) == 2); + RUBY_ASSERT(rb_darray_size(feature_indexes) == 2); // assert feature_indexes does not look like a special const - assert(!SPECIAL_CONST_P((VALUE)feature_indexes)); + RUBY_ASSERT(!SPECIAL_CONST_P((VALUE)feature_indexes)); *value = (st_data_t)feature_indexes; } @@ -355,6 +371,13 @@ loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg) return ST_DELETE; } +void +rb_free_loaded_features_index(rb_vm_t *vm) +{ + st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0); + st_free_table(vm->loaded_features_index); +} + static st_table * get_loaded_features_index(rb_vm_t *vm) { @@ -475,6 +498,12 @@ loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f) return ST_STOP; } +/* + * Returns the type of already provided feature. + * 'r': ruby script (".rb") + * 's': shared object (".so"/"."DLEXT) + * 'u': unsuffixed + */ static int rb_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn) { @@ -689,6 +718,19 @@ rb_provide(const char *feature) NORETURN(static void load_failed(VALUE)); +static inline VALUE +realpath_internal_cached(VALUE hash, VALUE path) +{ + VALUE ret = rb_hash_aref(hash, path); + if(RTEST(ret)) { + return ret; + } + + VALUE realpath = rb_realpath_internal(Qnil, path, 1); + rb_hash_aset(hash, rb_fstring(path), rb_fstring(realpath)); + return realpath; +} + static inline void load_iseq_eval(rb_execution_context_t *ec, VALUE fname) { @@ -697,13 +739,41 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname) if (!iseq) { rb_execution_context_t *ec = GET_EC(); VALUE v = rb_vm_push_frame_fname(ec, fname); - rb_ast_t *ast; - VALUE parser = rb_parser_new(); - rb_parser_set_context(parser, NULL, FALSE); - ast = (rb_ast_t *)rb_parser_load_file(parser, fname); - iseq = rb_iseq_new_top(&ast->body, rb_fstring_lit("<top (required)>"), - fname, rb_realpath_internal(Qnil, fname, 1), NULL); - rb_ast_dispose(ast); + + rb_thread_t *th = rb_ec_thread_ptr(ec); + VALUE realpath_map = get_loaded_features_realpath_map(th->vm); + + if (*rb_ruby_prism_ptr()) { + pm_parse_result_t result = { 0 }; + result.options.line = 1; + result.node.coverage_enabled = 1; + + VALUE error = pm_load_parse_file(&result, fname); + + if (error == Qnil) { + iseq = pm_iseq_new_top(&result.node, rb_fstring_lit("<top (required)>"), fname, realpath_internal_cached(realpath_map, fname), NULL); + pm_parse_result_free(&result); + } + else { + rb_vm_pop_frame(ec); + RB_GC_GUARD(v); + pm_parse_result_free(&result); + rb_exc_raise(error); + } + } + else { + rb_ast_t *ast; + VALUE ast_value; + VALUE parser = rb_parser_new(); + rb_parser_set_context(parser, NULL, FALSE); + ast_value = rb_parser_load_file(parser, fname); + ast = rb_ruby_ast_data_get(ast_value); + + iseq = rb_iseq_new_top(ast_value, rb_fstring_lit("<top (required)>"), + fname, realpath_internal_cached(realpath_map, fname), NULL); + rb_ast_dispose(ast); + } + rb_vm_pop_frame(ec); RB_GC_GUARD(v); } @@ -820,9 +890,8 @@ rb_load_protect(VALUE fname, int wrap, int *pstate) * LoadError will be raised. * * If the optional _wrap_ parameter is +true+, the loaded script will - * be executed under an anonymous module, protecting the calling - * program's global namespace. If the optional _wrap_ parameter is a - * module, the loaded script will be executed under the given module. + * be executed under an anonymous module. If the optional _wrap_ parameter + * is a module, the loaded script will be executed under the given module. * In no circumstance will any local variables in the loaded file be * propagated to the loading environment. */ @@ -907,6 +976,7 @@ load_unlock(rb_vm_t *vm, const char *ftptr, int done) } } +static VALUE rb_require_string_internal(VALUE fname, bool resurrect); /* * call-seq: @@ -921,15 +991,14 @@ load_unlock(rb_vm_t *vm, const char *ftptr, int done) * If the filename starts with './' or '../', resolution is based on Dir.pwd. * * If the filename has the extension ".rb", it is loaded as a source file; if - * the extension is ".so", ".o", or ".dll", or the default shared library - * extension on the current platform, Ruby loads the shared library as a - * Ruby extension. Otherwise, Ruby tries adding ".rb", ".so", and so on - * to the name until found. If the file named cannot be found, a LoadError - * will be raised. + * the extension is ".so", ".o", or the default shared library extension on + * the current platform, Ruby loads the shared library as a Ruby extension. + * Otherwise, Ruby tries adding ".rb", ".so", and so on to the name until + * found. If the file named cannot be found, a LoadError will be raised. * - * For Ruby extensions the filename given may use any shared library - * extension. For example, on Linux the socket extension is "socket.so" and - * <code>require 'socket.dll'</code> will load the socket extension. + * For Ruby extensions the filename given may use ".so" or ".o". For example, + * on macOS the socket extension is "socket.bundle" and + * <code>require 'socket.so'</code> will load the socket extension. * * The absolute path of the loaded file is added to * <code>$LOADED_FEATURES</code> (<code>$"</code>). A file will not be @@ -969,7 +1038,7 @@ rb_f_require_relative(VALUE obj, VALUE fname) rb_loaderror("cannot infer basepath"); } base = rb_file_dirname(base); - return rb_require_string(rb_file_absolute_path(fname, base)); + return rb_require_string_internal(rb_file_absolute_path(fname, base), false); } typedef int (*feature_func)(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn); @@ -979,7 +1048,7 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_ { VALUE tmp; char *ext, *ftptr; - int type, ft = 0; + int ft = 0; const char *loading; *path = 0; @@ -1031,11 +1100,11 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_ return 'r'; } tmp = fname; - type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext); + const unsigned int type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext); // Check if it's a statically linked extension when // not already a feature and not found as a dynamic library. - if (!ft && type != 1 && vm->static_ext_inits) { + if (!ft && type != loadable_ext_rb && vm->static_ext_inits) { VALUE lookup_name = tmp; // Append ".so" if not already present so for example "etc" can find "etc.so". // We always register statically linked extensions with a ".so" extension. @@ -1063,13 +1132,13 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_ goto feature_present; } /* fall through */ - case 1: + case loadable_ext_rb: ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (rb_feature_p(vm, ftptr, ext, !--type, TRUE, &loading) && !loading) + if (rb_feature_p(vm, ftptr, ext, type == loadable_ext_rb, TRUE, &loading) && !loading) break; *path = tmp; } - return type ? 's' : 'r'; + return type > loadable_ext_rb ? 's' : 'r'; feature_present: if (loading) *path = rb_filesystem_str_new_cstr(loading); @@ -1167,8 +1236,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa rb_thread_t *th = rb_ec_thread_ptr(ec); volatile const struct { VALUE wrapper, self, errinfo; + rb_execution_context_t *ec; } saved = { th->top_wrapper, th->top_self, ec->errinfo, + ec, }; enum ruby_tag_type state; char *volatile ftptr = 0; @@ -1180,7 +1251,6 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa volatile bool reset_ext_config = false; struct rb_ext_config prev_ext_config; - fname = rb_get_path(fname); path = rb_str_encode_ospath(fname); RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname)); saved_path = path; @@ -1189,7 +1259,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa ec->errinfo = Qnil; /* ensure */ th->top_wrapper = 0; if ((state = EC_EXEC_TAG()) == TAG_NONE) { - long handle; + VALUE handle; int found; RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname)); @@ -1208,7 +1278,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa result = TAG_RETURN; } else if (RTEST(rb_hash_aref(realpaths, - realpath = rb_realpath_internal(Qnil, path, 1)))) { + realpath = realpath_internal_cached(realpath_map, path)))) { result = 0; } else { @@ -1220,9 +1290,9 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa case 's': reset_ext_config = true; ext_config_push(th, &prev_ext_config); - handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext, - path, VM_BLOCK_HANDLER_NONE, path); - rb_ary_push(ruby_dln_librefs, LONG2NUM(handle)); + handle = rb_vm_call_cfunc(rb_vm_top_self(), load_ext, + path, VM_BLOCK_HANDLER_NONE, path); + rb_hash_aset(ruby_dln_libmap, path, SVALUE2NUM((SIGNED_VALUE)handle)); break; } result = TAG_RETURN; @@ -1231,6 +1301,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa } EC_POP_TAG(); + ec = saved.ec; rb_thread_t *th2 = rb_ec_thread_ptr(ec); th2->top_self = saved.self; th2->top_wrapper = saved.wrapper; @@ -1268,7 +1339,6 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa if (real) { real = rb_fstring(real); rb_hash_aset(realpaths, real, Qtrue); - rb_hash_aset(realpath_map, path, real); } } ec->errinfo = saved.errinfo; @@ -1306,6 +1376,12 @@ ruby_require_internal(const char *fname, unsigned int len) VALUE rb_require_string(VALUE fname) { + return rb_require_string_internal(FilePathValue(fname), false); +} + +static VALUE +rb_require_string_internal(VALUE fname, bool resurrect) +{ rb_execution_context_t *ec = GET_EC(); int result = require_internal(ec, fname, 1, RTEST(ruby_verbose)); @@ -1313,6 +1389,7 @@ rb_require_string(VALUE fname) EC_JUMP_TAG(ec, result); } if (result < 0) { + if (resurrect) fname = rb_str_resurrect(fname); load_failed(fname); } @@ -1322,7 +1399,9 @@ rb_require_string(VALUE fname) VALUE rb_require(const char *fname) { - return rb_require_string(rb_str_new_cstr(fname)); + struct RString fake; + VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0); + return rb_require_string_internal(str, true); } static int @@ -1456,10 +1535,22 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file) * autoload?(name, inherit=true) -> String or nil * * Returns _filename_ to be loaded if _name_ is registered as - * +autoload+. + * +autoload+ in the current namespace or one of its ancestors. * * autoload(:B, "b") * autoload?(:B) #=> "b" + * + * module C + * autoload(:D, "d") + * autoload?(:D) #=> "d" + * autoload?(:B) #=> nil + * end + * + * class E + * autoload(:F, "f") + * autoload?(:F) #=> "f" + * autoload?(:B) #=> "b" + * end */ static VALUE @@ -1473,6 +1564,37 @@ rb_f_autoload_p(int argc, VALUE *argv, VALUE obj) return rb_mod_autoload_p(argc, argv, klass); } +void * +rb_ext_resolve_symbol(const char* fname, const char* symbol) +{ + VALUE handle; + VALUE resolved; + VALUE path; + char *ext; + VALUE fname_str = rb_str_new_cstr(fname); + + resolved = rb_resolve_feature_path((VALUE)NULL, fname_str); + if (NIL_P(resolved)) { + ext = strrchr(fname, '.'); + if (!ext || !IS_SOEXT(ext)) { + rb_str_cat_cstr(fname_str, ".so"); + } + if (rb_feature_p(GET_VM(), fname, 0, FALSE, FALSE, 0)) { + return dln_symbol(NULL, symbol); + } + return NULL; + } + if (RARRAY_LEN(resolved) != 2 || rb_ary_entry(resolved, 0) != ID2SYM(rb_intern("so"))) { + return NULL; + } + path = rb_ary_entry(resolved, 1); + handle = rb_hash_lookup(ruby_dln_libmap, path); + if (NIL_P(handle)) { + return NULL; + } + return dln_symbol((void *)NUM2SVALUE(handle), symbol); +} + void Init_load(void) { @@ -1507,6 +1629,6 @@ Init_load(void) rb_define_global_function("autoload", rb_f_autoload, 2); rb_define_global_function("autoload?", rb_f_autoload_p, -1); - ruby_dln_librefs = rb_ary_hidden_new(0); - rb_gc_register_mark_object(ruby_dln_librefs); + ruby_dln_libmap = rb_hash_new_with_size(0); + rb_vm_register_global_object(ruby_dln_libmap); } |