Fix autoload status of statically linked extensions
Previously, for statically-linked extensions, we used `vm->loading_table` to delay calling the init function until the extensions are required. This caused the extensions to look like they are in the middle of being loaded even before they're required. (`rb_feature_p()` returned true with a loading path output.) Combined with autoload, queries like `defined?(CONST)` and `Module#autoload?` were confused by this and returned nil incorrectly. RubyGems uses `defined?` to detect if OpenSSL is available and failed when OpenSSL was available in builds using `--with-static-linked-ext`. Use a dedicated table for the init functions instead of adding them to the loading table. This lets us remove some logic from non-EXTSTATIC builds. [Bug #19115]
This commit is contained in:
parent
e15cd01149
commit
790cf4b6d0
Notes:
git
2022-11-25 21:22:00 +00:00
55
load.c
55
load.c
@ -850,14 +850,6 @@ load_lock(rb_vm_t *vm, const char *ftptr, bool warn)
|
|||||||
st_insert(loading_tbl, (st_data_t)ftptr, data);
|
st_insert(loading_tbl, (st_data_t)ftptr, data);
|
||||||
return (char *)ftptr;
|
return (char *)ftptr;
|
||||||
}
|
}
|
||||||
else if (imemo_type_p(data, imemo_memo)) {
|
|
||||||
struct MEMO *memo = MEMO_CAST(data);
|
|
||||||
void (*init)(void) = memo->u3.func;
|
|
||||||
data = (st_data_t)rb_thread_shield_new();
|
|
||||||
st_insert(loading_tbl, (st_data_t)ftptr, data);
|
|
||||||
(*init)();
|
|
||||||
return (char *)"";
|
|
||||||
}
|
|
||||||
if (warn) {
|
if (warn) {
|
||||||
VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
|
VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
|
||||||
rb_backtrace_each(rb_str_append, warning);
|
rb_backtrace_each(rb_str_append, warning);
|
||||||
@ -1025,6 +1017,23 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_
|
|||||||
}
|
}
|
||||||
tmp = fname;
|
tmp = fname;
|
||||||
type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
|
type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
|
||||||
|
#if EXTSTATIC
|
||||||
|
if (!ft && type != 1) { // not already a feature and not found as a dynamic library
|
||||||
|
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.
|
||||||
|
// See encinit.c and extinit.c (generated at build-time).
|
||||||
|
if (!ext) {
|
||||||
|
lookup_name = rb_str_dup(lookup_name);
|
||||||
|
rb_str_cat_cstr(lookup_name, ".so");
|
||||||
|
}
|
||||||
|
ftptr = RSTRING_PTR(lookup_name);
|
||||||
|
if (st_lookup(vm->static_ext_inits, (st_data_t)ftptr, NULL)) {
|
||||||
|
*path = rb_filesystem_str_new_cstr(ftptr);
|
||||||
|
return 's';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0:
|
case 0:
|
||||||
if (ft)
|
if (ft)
|
||||||
@ -1063,6 +1072,20 @@ load_ext(VALUE path)
|
|||||||
return (VALUE)dln_load(RSTRING_PTR(path));
|
return (VALUE)dln_load(RSTRING_PTR(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if EXTSTATIC
|
||||||
|
static bool
|
||||||
|
run_static_ext_init(rb_vm_t *vm, const char *feature)
|
||||||
|
{
|
||||||
|
st_data_t key = (st_data_t)feature;
|
||||||
|
st_data_t init_func;
|
||||||
|
if (st_delete(vm->static_ext_inits, &key, &init_func)) {
|
||||||
|
((void (*)(void))init_func)();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
no_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn)
|
no_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn)
|
||||||
{
|
{
|
||||||
@ -1164,6 +1187,11 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
|
|||||||
else if (!*ftptr) {
|
else if (!*ftptr) {
|
||||||
result = TAG_RETURN;
|
result = TAG_RETURN;
|
||||||
}
|
}
|
||||||
|
#if EXTSTATIC
|
||||||
|
else if (found == 's' && run_static_ext_init(th->vm, RSTRING_PTR(path))) {
|
||||||
|
result = TAG_RETURN;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else if (RTEST(rb_hash_aref(realpaths,
|
else if (RTEST(rb_hash_aref(realpaths,
|
||||||
realpath = rb_realpath_internal(Qnil, path, 1)))) {
|
realpath = rb_realpath_internal(Qnil, path, 1)))) {
|
||||||
result = 0;
|
result = 0;
|
||||||
@ -1280,6 +1308,7 @@ rb_require(const char *fname)
|
|||||||
return rb_require_string(rb_str_new_cstr(fname));
|
return rb_require_string(rb_str_new_cstr(fname));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if EXTSTATIC
|
||||||
static int
|
static int
|
||||||
register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing)
|
register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing)
|
||||||
{
|
{
|
||||||
@ -1289,22 +1318,22 @@ register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing
|
|||||||
rb_warn("%s is already registered", name);
|
rb_warn("%s is already registered", name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*value = (st_data_t)MEMO_NEW(0, 0, init);
|
*value = (st_data_t)init;
|
||||||
*key = (st_data_t)ruby_strdup(name);
|
|
||||||
}
|
}
|
||||||
return ST_CONTINUE;
|
return ST_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
RUBY_FUNC_EXPORTED void
|
void
|
||||||
ruby_init_ext(const char *name, void (*init)(void))
|
ruby_init_ext(const char *name, void (*init)(void))
|
||||||
{
|
{
|
||||||
rb_vm_t *vm = GET_VM();
|
rb_vm_t *vm = GET_VM();
|
||||||
st_table *loading_tbl = get_loading_table(vm);
|
st_table *inits_table = vm->static_ext_inits;
|
||||||
|
|
||||||
if (feature_provided(vm, name, 0))
|
if (feature_provided(vm, name, 0))
|
||||||
return;
|
return;
|
||||||
st_update(loading_tbl, (st_data_t)name, register_init_ext, (st_data_t)init);
|
st_update(inits_table, (st_data_t)name, register_init_ext, (st_data_t)init);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
|
@ -65,6 +65,24 @@ p Foo::Bar
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_autoload_p_with_static_extensions
|
||||||
|
require 'rbconfig'
|
||||||
|
omit unless RbConfig::CONFIG['EXTSTATIC'] == 'static'
|
||||||
|
begin
|
||||||
|
require 'fcntl.so'
|
||||||
|
rescue LoadError
|
||||||
|
omit('fcntl not included in the build')
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_separately(['--disable-all'], <<~RUBY)
|
||||||
|
autoload :Fcntl, 'fcntl.so'
|
||||||
|
|
||||||
|
assert_equal('fcntl.so', autoload?(:Fcntl))
|
||||||
|
assert(Object.const_defined?(:Fcntl))
|
||||||
|
assert_equal('constant', defined?(Fcntl), '[Bug #19115]')
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
def test_autoload_with_unqualified_file_name # [ruby-core:69206]
|
def test_autoload_with_unqualified_file_name # [ruby-core:69206]
|
||||||
Object.send(:remove_const, :A) if Object.const_defined?(:A)
|
Object.send(:remove_const, :A) if Object.const_defined?(:A)
|
||||||
|
|
||||||
|
3
vm.c
3
vm.c
@ -4005,6 +4005,9 @@ Init_vm_objects(void)
|
|||||||
vm->mark_object_ary = rb_ary_hidden_new(128);
|
vm->mark_object_ary = rb_ary_hidden_new(128);
|
||||||
vm->loading_table = st_init_strtable();
|
vm->loading_table = st_init_strtable();
|
||||||
vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000);
|
vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000);
|
||||||
|
#if EXTSTATIC
|
||||||
|
vm->static_ext_inits = st_init_strtable();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_MMAP
|
#ifdef HAVE_MMAP
|
||||||
vm->shape_list = (rb_shape_t *)mmap(NULL, rb_size_mul_or_raise(SHAPE_BITMAP_SIZE * 32, sizeof(rb_shape_t), rb_eRuntimeError),
|
vm->shape_list = (rb_shape_t *)mmap(NULL, rb_size_mul_or_raise(SHAPE_BITMAP_SIZE * 32, sizeof(rb_shape_t), rb_eRuntimeError),
|
||||||
|
@ -538,6 +538,10 @@ struct rb_iseq_struct {
|
|||||||
|
|
||||||
#define ISEQ_BODY(iseq) ((iseq)->body)
|
#define ISEQ_BODY(iseq) ((iseq)->body)
|
||||||
|
|
||||||
|
#ifndef EXTSTATIC
|
||||||
|
#define EXTSTATIC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef USE_LAZY_LOAD
|
#ifndef USE_LAZY_LOAD
|
||||||
#define USE_LAZY_LOAD 0
|
#define USE_LAZY_LOAD 0
|
||||||
#endif
|
#endif
|
||||||
@ -704,6 +708,11 @@ typedef struct rb_vm_struct {
|
|||||||
VALUE loaded_features_realpaths;
|
VALUE loaded_features_realpaths;
|
||||||
struct st_table *loaded_features_index;
|
struct st_table *loaded_features_index;
|
||||||
struct st_table *loading_table;
|
struct st_table *loading_table;
|
||||||
|
#if EXTSTATIC
|
||||||
|
// For running the init function of statically linked
|
||||||
|
// extensions when they are loaded
|
||||||
|
struct st_table *static_ext_inits;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* signal */
|
/* signal */
|
||||||
struct {
|
struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user