diff options
| author | nagachika <nagachika@ruby-lang.org> | 2023-03-25 14:25:37 +0900 |
|---|---|---|
| committer | nagachika <nagachika@ruby-lang.org> | 2023-03-25 14:25:37 +0900 |
| commit | 5c5a1135b2e688eca7dbc7ce085f37a4d9fa3fd1 (patch) | |
| tree | 124217702180115e71eac2b0931173018bf974dd | |
| parent | a308900b74ff897de50dc9c212c03daaf642f003 (diff) | |
merge revision(s) 790cf4b6d0475614afb127b416e87cfa39044d67: [Backport #19115]
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]
---
load.c | 55 +++++++++++++++++++++++++++++++++++-----------
test/ruby/test_autoload.rb | 18 +++++++++++++++
vm.c | 3 +++
vm_core.h | 9 ++++++++
4 files changed, 72 insertions(+), 13 deletions(-)
| -rw-r--r-- | load.c | 55 | ||||
| -rw-r--r-- | test/ruby/test_autoload.rb | 18 | ||||
| -rw-r--r-- | version.h | 2 | ||||
| -rw-r--r-- | vm.c | 3 | ||||
| -rw-r--r-- | vm_core.h | 9 |
5 files changed, 73 insertions, 14 deletions
@@ -813,14 +813,6 @@ load_lock(rb_vm_t *vm, const char *ftptr, bool warn) st_insert(loading_tbl, (st_data_t)ftptr, data); 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 && rb_thread_shield_owned((VALUE)data)) { VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr); rb_backtrace_each(rb_str_append, warning); @@ -987,6 +979,23 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_ } tmp = fname; 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) { case 0: if (ft) @@ -1025,6 +1034,20 @@ load_ext(VALUE 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 no_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn) { @@ -1126,6 +1149,11 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa else if (!*ftptr) { 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, realpath = rb_realpath_internal(Qnil, path, 1)))) { result = 0; @@ -1242,6 +1270,7 @@ rb_require(const char *fname) return rb_require_string(rb_str_new_cstr(fname)); } +#if EXTSTATIC static int register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing) { @@ -1251,22 +1280,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); } else { - *value = (st_data_t)MEMO_NEW(0, 0, init); - *key = (st_data_t)ruby_strdup(name); + *value = (st_data_t)init; } return ST_CONTINUE; } -RUBY_FUNC_EXPORTED void +void ruby_init_ext(const char *name, void (*init)(void)) { 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)) 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: diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb index 7709760d19..7010645317 100644 --- a/test/ruby/test_autoload.rb +++ b/test/ruby/test_autoload.rb @@ -65,6 +65,24 @@ p Foo::Bar } 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] Object.send(:remove_const, :A) if Object.const_defined?(:A) @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 4 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 215 +#define RUBY_PATCHLEVEL 216 #define RUBY_RELEASE_YEAR 2023 #define RUBY_RELEASE_MONTH 3 @@ -3831,6 +3831,9 @@ Init_vm_objects(void) vm->mark_object_ary = rb_ary_tmp_new(128); vm->loading_table = st_init_strtable(); vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000); +#if EXTSTATIC + vm->static_ext_inits = st_init_strtable(); +#endif } /* top self */ @@ -518,6 +518,10 @@ struct rb_iseq_struct { } aux; }; +#ifndef EXTSTATIC +#define EXTSTATIC 0 +#endif + #ifndef USE_LAZY_LOAD #define USE_LAZY_LOAD 0 #endif @@ -679,6 +683,11 @@ typedef struct rb_vm_struct { VALUE loaded_features_realpaths; struct st_table *loaded_features_index; 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 */ struct { |
