summaryrefslogtreecommitdiff
path: root/load.c
diff options
context:
space:
mode:
Diffstat (limited to 'load.c')
-rw-r--r--load.c202
1 files changed, 162 insertions, 40 deletions
diff --git a/load.c b/load.c
index da81becfc1..af2475f999 100644
--- a/load.c
+++ b/load.c
@@ -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);
}