From 43392afb122819e568f35c6a8e528580b9604411 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 23 Sep 2025 20:52:52 +0900 Subject: [PATCH 01/28] ripper: Fix dependency for generated ripper sources Missed at c89f5191706549bb1d7e0277fc07a413714ddecc. --- common.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/common.mk b/common.mk index 1b68736a18e398..15e314cd1bbe8a 100644 --- a/common.mk +++ b/common.mk @@ -1316,6 +1316,7 @@ $(common_mk__artifact_): $(srcdir)/common.mk $(common_mk_includes) ripper_srcs: $(RIPPER_SRCS) $(RIPPER_SRCS): $(srcdir)/parse.y $(srcdir)/defs/id.def +$(RIPPER_SRCS): $(srcdir)/ext/ripper/depend $(srcdir)/ext/ripper/extconf.rb $(RIPPER_SRCS): $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/ext/ripper/tools/dsl.rb $(RIPPER_SRCS): $(srcdir)/ext/ripper/ripper_init.c.tmpl $(srcdir)/ext/ripper/eventids2.c $(ECHO) generating $@ From 4f47327287c00836a9826805a8799678d2c18516 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 15 Jun 2025 18:55:57 +0900 Subject: [PATCH 02/28] Update current namespace management by using control frames and lexical contexts to fix inconsistent and wrong current namespace detections. This includes: * Moving load_path and related things from rb_vm_t to rb_namespace_t to simplify accessing those values via namespace (instead of accessing either vm or ns) * Initializing root_namespace earlier and consolidate builtin_namespace into root_namespace * Adding VM_FRAME_FLAG_NS_REQUIRE for checkpoints to detect a namespace to load/require files * Removing implicit refinements in the root namespace which was used to determine the namespace to be loaded (replaced by VM_FRAME_FLAG_NS_REQUIRE) * Removing namespaces from rb_proc_t because its namespace can be identified by lexical context * Starting to use ep[VM_ENV_DATA_INDEX_SPECVAL] to store the current namespace when the frame type is MAGIC_TOP or MAGIC_CLASS (block handlers don't exist in this case) --- builtin.c | 11 +- class.c | 2 +- eval.c | 1 + eval_intern.h | 5 +- inits.c | 2 +- insns.def | 3 +- internal/class.h | 16 +- internal/inits.h | 3 + internal/namespace.h | 21 +-- iseq.c | 2 +- load.c | 372 ++++++++++++------------------------ mini_builtin.c | 19 +- namespace.c | 437 +++++++++++++++---------------------------- proc.c | 2 - ruby.c | 16 +- variable.c | 66 +------ vm.c | 225 +++++++++++++--------- vm_core.h | 57 +++--- vm_dump.c | 16 +- vm_insnhelper.c | 21 --- vm_insnhelper.h | 2 +- 21 files changed, 487 insertions(+), 812 deletions(-) diff --git a/builtin.c b/builtin.c index 3400c4976ef526..158b98568530cb 100644 --- a/builtin.c +++ b/builtin.c @@ -50,17 +50,8 @@ load_with_builtin_functions(const char *feature_name, const struct rb_builtin_fu ASSUME(iseq); // otherwise an exception should have raised vm->builtin_function_table = NULL; - rb_namespace_enable_builtin(); - // exec - if (rb_namespace_available() && rb_mNamespaceRefiner) { - rb_iseq_eval_with_refinement(rb_iseq_check(iseq), rb_mNamespaceRefiner); - } - else { - rb_iseq_eval(rb_iseq_check(iseq)); - } - - rb_namespace_disable_builtin(); + rb_iseq_eval(rb_iseq_check(iseq), rb_root_namespace()); // builtin functions are loaded in the root namespace } void diff --git a/class.c b/class.c index b15f9f5ae7258d..e133d1579b3dd3 100644 --- a/class.c +++ b/class.c @@ -650,7 +650,7 @@ class_alloc0(enum ruby_value_type type, VALUE klass, bool namespaceable) { rb_ns_subclasses_t *ns_subclasses; rb_subclass_anchor_t *anchor; - const rb_namespace_t *ns = rb_definition_namespace(); + const rb_namespace_t *ns = rb_current_namespace(); if (!ruby_namespace_init_done) { namespaceable = true; diff --git a/eval.c b/eval.c index 019a2d19a25f3b..0ff59efd988975 100644 --- a/eval.c +++ b/eval.c @@ -80,6 +80,7 @@ ruby_setup(void) rb_vm_encoded_insn_data_table_init(); Init_enable_namespace(); Init_vm_objects(); + Init_root_namespace(); Init_fstring_table(); EC_PUSH_TAG(GET_EC()); diff --git a/eval_intern.h b/eval_intern.h index 2c244aa5e09b40..6353319c6ffe83 100644 --- a/eval_intern.h +++ b/eval_intern.h @@ -296,7 +296,10 @@ VALUE rb_vm_make_jump_tag_but_local_jump(enum ruby_tag_type state, VALUE val); rb_cref_t *rb_vm_cref(void); rb_cref_t *rb_vm_cref_replace_with_duplicated_cref(void); VALUE rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, VALUE block_handler, VALUE filename); -VALUE rb_vm_call_cfunc2(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg2, VALUE block_handler, VALUE filename); +VALUE rb_vm_call_cfunc_in_namespace(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg2, VALUE filename, const rb_namespace_t *ns); +void rb_vm_frame_flag_set_ns_require(const rb_execution_context_t *ec); +const rb_namespace_t *rb_vm_current_namespace(const rb_execution_context_t *ec); +const rb_namespace_t *rb_vm_loading_namespace(const rb_execution_context_t *ec); void rb_vm_set_progname(VALUE filename); VALUE rb_vm_cbase(void); diff --git a/inits.c b/inits.c index e0dab9e890fbd8..5209e429f996e9 100644 --- a/inits.c +++ b/inits.c @@ -52,6 +52,7 @@ rb_call_inits(void) CALL(Time); CALL(Random); CALL(load); + CALL(Namespace); CALL(Proc); CALL(Binding); CALL(Math); @@ -78,7 +79,6 @@ rb_call_inits(void) CALL(Prism); CALL(unicode_version); CALL(Set); - CALL(Namespace); // enable builtin loading CALL(builtin); diff --git a/insns.def b/insns.def index 35afe28a1a5905..239fe85aa51439 100644 --- a/insns.def +++ b/insns.def @@ -803,12 +803,13 @@ defineclass (VALUE val) { VALUE klass = vm_find_or_create_class_by_id(id, flags, cbase, super); + const rb_namespace_t *ns = rb_current_namespace(); rb_iseq_check(class_iseq); /* enter scope */ vm_push_frame(ec, class_iseq, VM_FRAME_MAGIC_CLASS | VM_ENV_FLAG_LOCAL, klass, - GET_BLOCK_HANDLER(), + GC_GUARDED_PTR(ns), (VALUE)vm_cref_push(ec, klass, NULL, FALSE, FALSE), ISEQ_BODY(class_iseq)->iseq_encoded, GET_SP(), ISEQ_BODY(class_iseq)->local_table_size, diff --git a/internal/class.h b/internal/class.h index bed69adef7cd1a..29b3526cf55cfd 100644 --- a/internal/class.h +++ b/internal/class.h @@ -390,8 +390,7 @@ RCLASS_EXT_READABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns) static inline rb_classext_t * RCLASS_EXT_READABLE_IN_NS(VALUE obj, const rb_namespace_t *ns) { - if (!ns - || NAMESPACE_BUILTIN_P(ns) + if (NAMESPACE_ROOT_P(ns) || RCLASS_PRIME_CLASSEXT_READABLE_P(obj)) { return RCLASS_EXT_PRIME(obj); } @@ -405,9 +404,9 @@ RCLASS_EXT_READABLE(VALUE obj) if (RCLASS_PRIME_CLASSEXT_READABLE_P(obj)) { return RCLASS_EXT_PRIME(obj); } - // delay namespace loading to optimize for unmodified classes + // delay determining the current namespace to optimize for unmodified classes ns = rb_current_namespace(); - if (!ns || NAMESPACE_BUILTIN_P(ns)) { + if (NAMESPACE_ROOT_P(ns)) { return RCLASS_EXT_PRIME(obj); } return RCLASS_EXT_READABLE_LOOKUP(obj, ns); @@ -440,8 +439,7 @@ RCLASS_EXT_WRITABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns) static inline rb_classext_t * RCLASS_EXT_WRITABLE_IN_NS(VALUE obj, const rb_namespace_t *ns) { - if (!ns - || NAMESPACE_BUILTIN_P(ns) + if (NAMESPACE_ROOT_P(ns) || RCLASS_PRIME_CLASSEXT_WRITABLE_P(obj)) { return RCLASS_EXT_PRIME(obj); } @@ -455,11 +453,9 @@ RCLASS_EXT_WRITABLE(VALUE obj) if (LIKELY(RCLASS_PRIME_CLASSEXT_WRITABLE_P(obj))) { return RCLASS_EXT_PRIME(obj); } - // delay namespace loading to optimize for unmodified classes + // delay determining the current namespace to optimize for unmodified classes ns = rb_current_namespace(); - if (!ns || NAMESPACE_BUILTIN_P(ns)) { - // If no namespace is specified, Ruby VM is in bootstrap - // and the clean class definition is under construction. + if (NAMESPACE_ROOT_P(ns)) { return RCLASS_EXT_PRIME(obj); } return RCLASS_EXT_WRITABLE_LOOKUP(obj, ns); diff --git a/internal/inits.h b/internal/inits.h index e618d87cc335d2..c1cf3db94d664c 100644 --- a/internal/inits.h +++ b/internal/inits.h @@ -32,6 +32,9 @@ void Init_enable_namespace(void); void Init_BareVM(void); void Init_vm_objects(void); +/* namespace.c */ +void Init_root_namespace(void); + /* vm_backtrace.c */ void Init_vm_backtrace(void); diff --git a/internal/namespace.h b/internal/namespace.h index 4cdfbc305fe1da..c68f0987aa814f 100644 --- a/internal/namespace.h +++ b/internal/namespace.h @@ -35,13 +35,14 @@ struct rb_namespace_struct { VALUE gvar_tbl; - bool is_builtin; bool is_user; bool is_optional; }; typedef struct rb_namespace_struct rb_namespace_t; -#define NAMESPACE_BUILTIN_P(ns) (ns && ns->is_builtin) +#define NAMESPACE_OBJ_P(obj) (CLASS_OF(obj) == rb_cNamespace) + +#define NAMESPACE_ROOT_P(ns) (ns && !ns->is_user) #define NAMESPACE_USER_P(ns) (ns && ns->is_user) #define NAMESPACE_OPTIONAL_P(ns) (ns && ns->is_optional) #define NAMESPACE_MAIN_P(ns) (ns && ns->is_user && !ns->is_optional) @@ -60,24 +61,16 @@ rb_namespace_available(void) return ruby_namespace_enabled; } -void rb_namespace_enable_builtin(void); -void rb_namespace_disable_builtin(void); -void rb_namespace_push_loading_namespace(const rb_namespace_t *); -void rb_namespace_pop_loading_namespace(const rb_namespace_t *); -rb_namespace_t * rb_root_namespace(void); -const rb_namespace_t *rb_builtin_namespace(void); -rb_namespace_t * rb_main_namespace(void); -const rb_namespace_t * rb_definition_namespace(void); -const rb_namespace_t * rb_loading_namespace(void); +const rb_namespace_t * rb_root_namespace(void); +const rb_namespace_t * rb_main_namespace(void); const rb_namespace_t * rb_current_namespace(void); -VALUE rb_current_namespace_details(VALUE); +const rb_namespace_t * rb_loading_namespace(void); void rb_namespace_entry_mark(void *); +void rb_namespace_gc_update_references(void *ptr); rb_namespace_t * rb_get_namespace_t(VALUE ns); VALUE rb_get_namespace_object(rb_namespace_t *ns); -typedef VALUE namespace_exec_func(VALUE arg); -VALUE rb_namespace_exec(const rb_namespace_t *ns, namespace_exec_func *func, VALUE arg); VALUE rb_namespace_local_extension(VALUE namespace, VALUE fname, VALUE path); diff --git a/iseq.c b/iseq.c index 082ea28c4ad199..ad9149ef98e815 100644 --- a/iseq.c +++ b/iseq.c @@ -1966,7 +1966,7 @@ iseqw_eval(VALUE self) if (0 == ISEQ_BODY(iseq)->iseq_size) { rb_raise(rb_eTypeError, "attempt to evaluate dummy InstructionSequence"); } - return rb_iseq_eval(iseq); + return rb_iseq_eval(iseq, rb_current_namespace()); } /* diff --git a/load.c b/load.c index 217d7bad847fa5..6f3da288aaafcc 100644 --- a/load.c +++ b/load.c @@ -23,8 +23,6 @@ #include "ractor_core.h" #include "vm_core.h" -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) @@ -39,38 +37,6 @@ static VALUE ruby_dln_libmap; # error Need integer for VALUE #endif -#define IS_NAMESPACE(obj) (CLASS_OF(obj) == rb_cNamespace) - -struct vm_and_namespace_struct { - rb_vm_t *vm; - rb_namespace_t *ns; -}; -typedef struct vm_and_namespace_struct vm_ns_t; -#define GET_vm_ns() vm_ns_t vm_ns_v = { .vm = GET_VM(), .ns = (rb_namespace_t *)rb_current_namespace(), }; vm_ns_t *vm_ns = &vm_ns_v; -#define GET_loading_vm_ns() vm_ns_t vm_ns_v = { .vm = GET_VM(), .ns = (rb_namespace_t *)rb_loading_namespace(), }; vm_ns_t *vm_ns = &vm_ns_v; - -#define CURRENT_NS_attr(vm_ns, attr) (NAMESPACE_USER_P(vm_ns->ns) ? vm_ns->ns->attr : vm_ns->vm->attr) -#define SET_NS_attr(vm_ns, attr, value) do { \ - if (NAMESPACE_USER_P(vm_ns->ns)) { vm_ns->ns->attr = value; } \ - else { vm_ns->vm->attr = value; } \ -} while (0) - -#define SET_NS_LOAD_PATH_CHECK_CACHE(vm_ns, value) SET_NS_attr(vm_ns, load_path_check_cache, value) -#define SET_NS_EXPANDED_LOAD_PATH(vm_ns, value) SET_NS_attr(vm_ns, expanded_load_path, value) - -#define CURRENT_NS_LOAD_PATH(vm_ns) CURRENT_NS_attr(vm_ns, load_path) -#define CURRENT_NS_LOAD_PATH_SNAPSHOT(vm_ns) CURRENT_NS_attr(vm_ns, load_path_snapshot) -#define CURRENT_NS_LOAD_PATH_CHECK_CACHE(vm_ns) CURRENT_NS_attr(vm_ns, load_path_check_cache) -#define CURRENT_NS_EXPANDED_LOAD_PATH(vm_ns) CURRENT_NS_attr(vm_ns, expanded_load_path) -#define CURRENT_NS_LOADING_TABLE(vm_ns) CURRENT_NS_attr(vm_ns, loading_table) -#define CURRENT_NS_LOADED_FEATURES(vm_ns) CURRENT_NS_attr(vm_ns, loaded_features) -#define CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns) CURRENT_NS_attr(vm_ns, loaded_features_snapshot) -#define CURRENT_NS_LOADED_FEATURES_REALPATHS(vm_ns) CURRENT_NS_attr(vm_ns, loaded_features_realpaths) -#define CURRENT_NS_LOADED_FEATURES_REALPATH_MAP(vm_ns) CURRENT_NS_attr(vm_ns, loaded_features_realpath_map) -#define CURRENT_NS_LOADED_FEATURES_INDEX(vm_ns) CURRENT_NS_attr(vm_ns, loaded_features_index) - -#define CURRENT_NS_RUBY_DLN_LIBMAP(vm_ns, map) (NAMESPACE_USER_P(vm_ns->ns) ? vm_ns->ns->ruby_dln_libmap : map) - enum { loadable_ext_rb = (0+ /* .rb extension is the first in both tables */ 1) /* offset by rb_find_file_ext() */ @@ -99,10 +65,10 @@ enum expand_type { string objects in $LOAD_PATH are frozen. */ static void -rb_construct_expanded_load_path(vm_ns_t *vm_ns, enum expand_type type, int *has_relative, int *has_non_cache) +rb_construct_expanded_load_path(rb_namespace_t *ns, enum expand_type type, int *has_relative, int *has_non_cache) { - VALUE load_path = CURRENT_NS_LOAD_PATH(vm_ns); - VALUE expanded_load_path = CURRENT_NS_EXPANDED_LOAD_PATH(vm_ns); + VALUE load_path = ns->load_path; + VALUE expanded_load_path = ns->expanded_load_path; VALUE snapshot; VALUE ary; long i; @@ -142,39 +108,39 @@ rb_construct_expanded_load_path(vm_ns_t *vm_ns, enum expand_type type, int *has_ rb_ary_push(ary, rb_fstring(expanded_path)); } rb_ary_freeze(ary); - SET_NS_EXPANDED_LOAD_PATH(vm_ns, ary); - snapshot = CURRENT_NS_LOAD_PATH_SNAPSHOT(vm_ns); - load_path = CURRENT_NS_LOAD_PATH(vm_ns); + ns->expanded_load_path = ary; + snapshot = ns->load_path_snapshot; + load_path = ns->load_path; rb_ary_replace(snapshot, load_path); } static VALUE -get_expanded_load_path(vm_ns_t *vm_ns) +get_expanded_load_path(rb_namespace_t *ns) { VALUE check_cache; const VALUE non_cache = Qtrue; - const VALUE load_path_snapshot = CURRENT_NS_LOAD_PATH_SNAPSHOT(vm_ns); - const VALUE load_path = CURRENT_NS_LOAD_PATH(vm_ns); + const VALUE load_path_snapshot = ns->load_path_snapshot; + const VALUE load_path = ns->load_path; if (!rb_ary_shared_with_p(load_path_snapshot, load_path)) { /* The load path was modified. Rebuild the expanded load path. */ int has_relative = 0, has_non_cache = 0; - rb_construct_expanded_load_path(vm_ns, EXPAND_ALL, &has_relative, &has_non_cache); + rb_construct_expanded_load_path(ns, EXPAND_ALL, &has_relative, &has_non_cache); if (has_relative) { - SET_NS_LOAD_PATH_CHECK_CACHE(vm_ns, rb_dir_getwd_ospath()); + ns->load_path_check_cache = rb_dir_getwd_ospath(); } else if (has_non_cache) { /* Non string object. */ - SET_NS_LOAD_PATH_CHECK_CACHE(vm_ns, non_cache); + ns->load_path_check_cache = non_cache; } else { - SET_NS_LOAD_PATH_CHECK_CACHE(vm_ns, 0); + ns->load_path_check_cache = 0; } } - else if ((check_cache = CURRENT_NS_LOAD_PATH_CHECK_CACHE(vm_ns)) == non_cache) { + else if ((check_cache = ns->load_path_check_cache) == non_cache) { int has_relative = 1, has_non_cache = 1; /* Expand only non-cacheable objects. */ - rb_construct_expanded_load_path(vm_ns, EXPAND_NON_CACHE, + rb_construct_expanded_load_path(ns, EXPAND_NON_CACHE, &has_relative, &has_non_cache); } else if (check_cache) { @@ -183,76 +149,49 @@ get_expanded_load_path(vm_ns_t *vm_ns) if (!rb_str_equal(check_cache, cwd)) { /* Current working directory or filesystem encoding was changed. Expand relative load path and non-cacheable objects again. */ - SET_NS_LOAD_PATH_CHECK_CACHE(vm_ns, cwd); - rb_construct_expanded_load_path(vm_ns, EXPAND_RELATIVE, + ns->load_path_check_cache = cwd; + rb_construct_expanded_load_path(ns, EXPAND_RELATIVE, &has_relative, &has_non_cache); } else { /* Expand only tilde (User HOME) and non-cacheable objects. */ - rb_construct_expanded_load_path(vm_ns, EXPAND_HOME, + rb_construct_expanded_load_path(ns, EXPAND_HOME, &has_relative, &has_non_cache); } } - return CURRENT_NS_EXPANDED_LOAD_PATH(vm_ns); + return ns->expanded_load_path; } VALUE rb_get_expanded_load_path(void) { - GET_loading_vm_ns(); - return get_expanded_load_path(vm_ns); -} - -static VALUE -load_path_getter(ID id, VALUE * p) -{ - GET_loading_vm_ns(); - return CURRENT_NS_LOAD_PATH(vm_ns); -} - -static VALUE -get_loaded_features(vm_ns_t *vm_ns) -{ - return CURRENT_NS_LOADED_FEATURES(vm_ns); -} - -static VALUE -get_loaded_features_realpaths(vm_ns_t *vm_ns) -{ - return CURRENT_NS_LOADED_FEATURES_REALPATHS(vm_ns); + return get_expanded_load_path((rb_namespace_t *)rb_loading_namespace()); } static VALUE -get_loaded_features_realpath_map(vm_ns_t *vm_ns) +load_path_getter(ID _x, VALUE * _y) { - return CURRENT_NS_LOADED_FEATURES_REALPATH_MAP(vm_ns); + return rb_loading_namespace()->load_path; } static VALUE get_LOADED_FEATURES(ID _x, VALUE *_y) { - GET_loading_vm_ns(); - return get_loaded_features(vm_ns); + return rb_loading_namespace()->loaded_features; } static void -reset_loaded_features_snapshot(vm_ns_t *vm_ns) +reset_loaded_features_snapshot(const rb_namespace_t *ns) { - VALUE snapshot = CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns); - VALUE loaded_features = CURRENT_NS_LOADED_FEATURES(vm_ns); + VALUE snapshot = ns->loaded_features_snapshot; + VALUE loaded_features = ns->loaded_features; rb_ary_replace(snapshot, loaded_features); } static struct st_table * -get_loaded_features_index_raw(vm_ns_t *vm_ns) -{ - return CURRENT_NS_LOADED_FEATURES_INDEX(vm_ns); -} - -static st_table * -get_loading_table(vm_ns_t *vm_ns) +get_loaded_features_index_raw(const rb_namespace_t *ns) { - return CURRENT_NS_LOADING_TABLE(vm_ns); + return ns->loaded_features_index; } static st_data_t @@ -273,7 +212,7 @@ is_rbext_path(VALUE feature_path) typedef rb_darray(long) feature_indexes_t; struct features_index_add_single_args { - vm_ns_t *vm_ns; + const rb_namespace_t *ns; VALUE offset; bool rb; }; @@ -282,7 +221,7 @@ static int features_index_add_single_callback(st_data_t *key, st_data_t *value, st_data_t raw_args, int existing) { struct features_index_add_single_args *args = (struct features_index_add_single_args *)raw_args; - vm_ns_t *vm_ns = args->vm_ns; + const rb_namespace_t *ns = args->ns; VALUE offset = args->offset; bool rb = args->rb; @@ -290,7 +229,7 @@ features_index_add_single_callback(st_data_t *key, st_data_t *value, st_data_t r VALUE this_feature_index = *value; if (FIXNUM_P(this_feature_index)) { - VALUE loaded_features = get_loaded_features(vm_ns); + VALUE loaded_features = ns->loaded_features; VALUE this_feature_path = RARRAY_AREF(loaded_features, FIX2LONG(this_feature_index)); feature_indexes_t feature_indexes; @@ -310,7 +249,7 @@ features_index_add_single_callback(st_data_t *key, st_data_t *value, st_data_t r long pos = -1; if (rb) { - VALUE loaded_features = get_loaded_features(vm_ns); + VALUE loaded_features = ns->loaded_features; for (size_t i = 0; i < rb_darray_size(feature_indexes); ++i) { long idx = rb_darray_get(feature_indexes, i); VALUE this_feature_path = RARRAY_AREF(loaded_features, idx); @@ -342,7 +281,7 @@ features_index_add_single_callback(st_data_t *key, st_data_t *value, st_data_t r } static void -features_index_add_single(vm_ns_t *vm_ns, const char* str, size_t len, VALUE offset, bool rb) +features_index_add_single(const rb_namespace_t *ns, const char* str, size_t len, VALUE offset, bool rb) { struct st_table *features_index; st_data_t short_feature_key; @@ -350,10 +289,10 @@ features_index_add_single(vm_ns_t *vm_ns, const char* str, size_t len, VALUE off Check_Type(offset, T_FIXNUM); short_feature_key = feature_key(str, len); - features_index = get_loaded_features_index_raw(vm_ns); + features_index = get_loaded_features_index_raw(ns); struct features_index_add_single_args args = { - .vm_ns = vm_ns, + .ns = ns, .offset = offset, .rb = rb, }; @@ -370,7 +309,7 @@ features_index_add_single(vm_ns_t *vm_ns, const char* str, size_t len, VALUE off relies on for its fast lookup. */ static void -features_index_add(vm_ns_t *vm_ns, VALUE feature, VALUE offset) +features_index_add(const rb_namespace_t *ns, VALUE feature, VALUE offset) { RUBY_ASSERT(rb_ractor_main_p()); @@ -398,14 +337,14 @@ features_index_add(vm_ns_t *vm_ns, VALUE feature, VALUE offset) if (p < feature_str) break; /* Now *p == '/'. We reach this point for every '/' in `feature`. */ - features_index_add_single(vm_ns, p + 1, feature_end - p - 1, offset, false); + features_index_add_single(ns, p + 1, feature_end - p - 1, offset, false); if (ext) { - features_index_add_single(vm_ns, p + 1, ext - p - 1, offset, rb); + features_index_add_single(ns, p + 1, ext - p - 1, offset, rb); } } - features_index_add_single(vm_ns, feature_str, feature_end - feature_str, offset, false); + features_index_add_single(ns, feature_str, feature_end - feature_str, offset, false); if (ext) { - features_index_add_single(vm_ns, feature_str, ext - feature_str, offset, rb); + features_index_add_single(ns, feature_str, ext - feature_str, offset, rb); } } @@ -419,31 +358,20 @@ 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) -{ - /* Destructs vm->loaded_features_index directly because this is only for - the VM destruction */ - 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(vm_ns_t *vm_ns) +get_loaded_features_index(const rb_namespace_t *ns) { int i; - VALUE features = CURRENT_NS_LOADED_FEATURES(vm_ns); - const VALUE snapshot = CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns); + VALUE features = ns->loaded_features; + const VALUE snapshot = ns->loaded_features_snapshot; if (!rb_ary_shared_with_p(snapshot, features)) { /* The sharing was broken; something (other than us in rb_provide_feature()) modified loaded_features. Rebuild the index. */ - st_foreach(CURRENT_NS_LOADED_FEATURES_INDEX(vm_ns), loaded_features_index_clear_i, 0); + st_foreach(ns->loaded_features_index, loaded_features_index_clear_i, 0); - VALUE realpaths = CURRENT_NS_LOADED_FEATURES_REALPATHS(vm_ns); - VALUE realpath_map = CURRENT_NS_LOADED_FEATURES_REALPATH_MAP(vm_ns); + VALUE realpaths = ns->loaded_features_realpaths; + VALUE realpath_map = ns->loaded_features_realpath_map; VALUE previous_realpath_map = rb_hash_dup(realpath_map); rb_hash_clear(realpaths); rb_hash_clear(realpath_map); @@ -459,15 +387,15 @@ get_loaded_features_index(vm_ns_t *vm_ns) as_str = rb_fstring(as_str); if (as_str != entry) rb_ary_store(features, i, as_str); - features_index_add(vm_ns, as_str, INT2FIX(i)); + features_index_add(ns, as_str, INT2FIX(i)); } /* The user modified $LOADED_FEATURES, so we should restore the changes. */ if (!rb_ary_shared_with_p(features, CURRENT_NS_LOADED_FEATURES(vm_ns))) { - rb_ary_replace(CURRENT_NS_LOADED_FEATURES(vm_ns), features); + rb_ary_replace(ns->loaded_features, features); } reset_loaded_features_snapshot(vm_ns); - features = CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns); + features = ns->loaded_features_snapshot; long j = RARRAY_LEN(features); for (i = 0; i < j; i++) { VALUE as_str = rb_ary_entry(features, i); @@ -481,7 +409,7 @@ get_loaded_features_index(vm_ns_t *vm_ns) rb_hash_aset(realpath_map, as_str, realpath); } } - return CURRENT_NS_LOADED_FEATURES_INDEX(vm_ns); + return ns->loaded_features_index; } /* This searches `load_path` for a value such that @@ -566,7 +494,7 @@ loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f) * 'u': unsuffixed */ static int -rb_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int expanded, const char **fn) +rb_feature_p(const rb_namespace_t *ns, const char *feature, const char *ext, int rb, int expanded, const char **fn) { VALUE features, this_feature_index = Qnil, v, p, load_path = 0; const char *f, *e; @@ -587,8 +515,8 @@ rb_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int e elen = 0; type = 0; } - features = get_loaded_features(vm_ns); - features_index = get_loaded_features_index(vm_ns); + features = ns->loaded_features; + features_index = get_loaded_features_index(ns); key = feature_key(feature, strlen(feature)); /* We search `features` for an entry such that either @@ -636,7 +564,7 @@ rb_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int e if ((n = RSTRING_LEN(v)) < len) continue; if (strncmp(f, feature, len) != 0) { if (expanded) continue; - if (!load_path) load_path = get_expanded_load_path(vm_ns); + if (!load_path) load_path = get_expanded_load_path((rb_namespace_t *)ns); if (!(p = loaded_feature_path(f, n, feature, len, type, load_path))) continue; expanded = 1; @@ -656,14 +584,14 @@ rb_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int e } } - loading_tbl = get_loading_table(vm_ns); + loading_tbl = ns->loading_table; f = 0; if (!expanded && !rb_is_absolute_path(feature)) { struct loaded_feature_searching fs; fs.name = feature; fs.len = len; fs.type = type; - fs.load_path = load_path ? load_path : get_expanded_load_path(vm_ns); + fs.load_path = load_path ? load_path : get_expanded_load_path((rb_namespace_t *)ns); fs.result = 0; st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs); if ((f = fs.result) != 0) { @@ -718,7 +646,7 @@ rb_provided(const char *feature) } static int -feature_provided(vm_ns_t *vm_ns, const char *feature, const char **loading) +feature_provided(rb_namespace_t *ns, const char *feature, const char **loading) { const char *ext = strrchr(feature, '.'); VALUE fullpath = 0; @@ -730,15 +658,15 @@ feature_provided(vm_ns_t *vm_ns, const char *feature, const char **loading) } if (ext && !strchr(ext, '/')) { if (IS_RBEXT(ext)) { - if (rb_feature_p(vm_ns, feature, ext, TRUE, FALSE, loading)) return TRUE; + if (rb_feature_p(ns, feature, ext, TRUE, FALSE, loading)) return TRUE; return FALSE; } else if (IS_SOEXT(ext) || IS_DLEXT(ext)) { - if (rb_feature_p(vm_ns, feature, ext, FALSE, FALSE, loading)) return TRUE; + if (rb_feature_p(ns, feature, ext, FALSE, FALSE, loading)) return TRUE; return FALSE; } } - if (rb_feature_p(vm_ns, feature, 0, TRUE, FALSE, loading)) + if (rb_feature_p(ns, feature, 0, TRUE, FALSE, loading)) return TRUE; RB_GC_GUARD(fullpath); return FALSE; @@ -747,30 +675,30 @@ feature_provided(vm_ns_t *vm_ns, const char *feature, const char **loading) int rb_feature_provided(const char *feature, const char **loading) { - GET_vm_ns(); - return feature_provided(vm_ns, feature, loading); + rb_namespace_t *ns = (rb_namespace_t *)rb_current_namespace(); + return feature_provided(ns, feature, loading); } static void -rb_provide_feature(vm_ns_t *vm_ns, VALUE feature) +rb_provide_feature(const rb_namespace_t *ns, VALUE feature) { VALUE features; - features = get_loaded_features(vm_ns); + features = ns->loaded_features; if (OBJ_FROZEN(features)) { rb_raise(rb_eRuntimeError, "$LOADED_FEATURES is frozen; cannot append feature"); } feature = rb_fstring(feature); - get_loaded_features_index(vm_ns); + get_loaded_features_index(ns); // If loaded_features and loaded_features_snapshot share the same backing // array, pushing into it would cause the whole array to be copied. // To avoid this we first clear loaded_features_snapshot. - rb_ary_clear(CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns)); + rb_ary_clear(ns->loaded_features_snapshot); rb_ary_push(features, feature); - features_index_add(vm_ns, feature, INT2FIX(RARRAY_LEN(features)-1)); - reset_loaded_features_snapshot(vm_ns); + features_index_add(ns, feature, INT2FIX(RARRAY_LEN(features)-1)); + reset_loaded_features_snapshot(ns); } void @@ -780,8 +708,7 @@ rb_provide(const char *feature) * rb_provide() must use rb_current_namespace to store provided features * in the current namespace's loaded_features, etc. */ - GET_vm_ns(); - rb_provide_feature(vm_ns, rb_fstring_cstr(feature)); + rb_provide_feature(rb_current_namespace(), rb_fstring_cstr(feature)); } NORETURN(static void load_failed(VALUE)); @@ -799,35 +726,17 @@ realpath_internal_cached(VALUE hash, VALUE path) return realpath; } -struct iseq_eval_in_namespace_data { - const rb_iseq_t *iseq; - bool in_builtin; -}; - -static VALUE -iseq_eval_in_namespace(VALUE arg) -{ - struct iseq_eval_in_namespace_data *data = (struct iseq_eval_in_namespace_data *)arg; - if (rb_namespace_available() && data->in_builtin) { - return rb_iseq_eval_with_refinement(data->iseq, rb_mNamespaceRefiner); - } - else { - return rb_iseq_eval(data->iseq); - } -} - static inline void load_iseq_eval(rb_execution_context_t *ec, VALUE fname) { - GET_loading_vm_ns(); - const rb_namespace_t *loading_ns = rb_loading_namespace(); + const rb_namespace_t *ns = rb_loading_namespace(); const rb_iseq_t *iseq = rb_iseq_load_iseq(fname); if (!iseq) { rb_execution_context_t *ec = GET_EC(); VALUE v = rb_vm_push_frame_fname(ec, fname); - VALUE realpath_map = get_loaded_features_realpath_map(vm_ns); + VALUE realpath_map = ns->loaded_features_realpath_map; if (rb_ruby_prism_p()) { pm_parse_result_t result = { 0 }; @@ -872,16 +781,7 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname) } rb_exec_event_hook_script_compiled(ec, iseq, Qnil); - if (loading_ns) { - struct iseq_eval_in_namespace_data arg = { - .iseq = iseq, - .in_builtin = NAMESPACE_BUILTIN_P(loading_ns), - }; - rb_namespace_exec(loading_ns, iseq_eval_in_namespace, (VALUE)&arg); - } - else { - rb_iseq_eval(iseq); - } + rb_iseq_eval(iseq, ns); } static inline enum ruby_tag_type @@ -899,7 +799,7 @@ load_wrapping(rb_execution_context_t *ec, VALUE fname, VALUE load_wrapper) ec->errinfo = Qnil; /* ensure */ /* load in module as toplevel */ - if (IS_NAMESPACE(load_wrapper)) { + if (NAMESPACE_OBJ_P(load_wrapper)) { ns = rb_get_namespace_t(load_wrapper); if (!ns->top_self) { ns->top_self = rb_obj_clone(rb_vm_top_self()); @@ -1058,10 +958,10 @@ rb_f_load(int argc, VALUE *argv, VALUE _) } static char * -load_lock(vm_ns_t *vm_ns, const char *ftptr, bool warn) +load_lock(const rb_namespace_t *ns, const char *ftptr, bool warn) { st_data_t data; - st_table *loading_tbl = get_loading_table(vm_ns); + st_table *loading_tbl = ns->loading_table; if (!st_lookup(loading_tbl, (st_data_t)ftptr, &data)) { /* partial state */ @@ -1103,11 +1003,11 @@ release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int exis } static void -load_unlock(vm_ns_t *vm_ns, const char *ftptr, int done) +load_unlock(const rb_namespace_t *ns, const char *ftptr, int done) { if (ftptr) { st_data_t key = (st_data_t)ftptr; - st_table *loading_tbl = get_loading_table(vm_ns); + st_table *loading_tbl = ns->loading_table; st_update(loading_tbl, key, release_thread_shield, done); } @@ -1186,10 +1086,10 @@ rb_f_require_relative(VALUE obj, VALUE fname) return rb_require_relative_entrypoint(fname); } -typedef int (*feature_func)(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int expanded, const char **fn); +typedef int (*feature_func)(const rb_namespace_t *ns, const char *feature, const char *ext, int rb, int expanded, const char **fn); static int -search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func rb_feature_p) +search_required(const rb_namespace_t *ns, VALUE fname, volatile VALUE *path, feature_func rb_feature_p) { VALUE tmp; char *ext, *ftptr; @@ -1200,20 +1100,20 @@ search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func ext = strrchr(ftptr = RSTRING_PTR(fname), '.'); if (ext && !strchr(ext, '/')) { if (IS_RBEXT(ext)) { - if (rb_feature_p(vm_ns, ftptr, ext, TRUE, FALSE, &loading)) { + if (rb_feature_p(ns, ftptr, ext, TRUE, FALSE, &loading)) { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 'r'; } if ((tmp = rb_find_file(fname)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (!rb_feature_p(vm_ns, ftptr, ext, TRUE, TRUE, &loading) || loading) + if (!rb_feature_p(ns, ftptr, ext, TRUE, TRUE, &loading) || loading) *path = tmp; return 'r'; } return 0; } else if (IS_SOEXT(ext)) { - if (rb_feature_p(vm_ns, ftptr, ext, FALSE, FALSE, &loading)) { + if (rb_feature_p(ns, ftptr, ext, FALSE, FALSE, &loading)) { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 's'; } @@ -1222,25 +1122,25 @@ search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func OBJ_FREEZE(tmp); if ((tmp = rb_find_file(tmp)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (!rb_feature_p(vm_ns, ftptr, ext, FALSE, TRUE, &loading) || loading) + if (!rb_feature_p(ns, ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; return 's'; } } else if (IS_DLEXT(ext)) { - if (rb_feature_p(vm_ns, ftptr, ext, FALSE, FALSE, &loading)) { + if (rb_feature_p(ns, ftptr, ext, FALSE, FALSE, &loading)) { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 's'; } if ((tmp = rb_find_file(fname)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (!rb_feature_p(vm_ns, ftptr, ext, FALSE, TRUE, &loading) || loading) + if (!rb_feature_p(ns, ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; return 's'; } } } - else if ((ft = rb_feature_p(vm_ns, ftptr, 0, FALSE, FALSE, &loading)) == 'r') { + else if ((ft = rb_feature_p(ns, ftptr, 0, FALSE, FALSE, &loading)) == 'r') { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 'r'; } @@ -1249,7 +1149,8 @@ search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func // Check if it's a statically linked extension when // not already a feature and not found as a dynamic library. - if (!ft && type != loadable_ext_rb && vm_ns->vm->static_ext_inits) { + rb_vm_t *vm = GET_VM(); + 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. @@ -1259,7 +1160,7 @@ search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func rb_str_cat_cstr(lookup_name, ".so"); } ftptr = RSTRING_PTR(lookup_name); - if (st_lookup(vm_ns->vm->static_ext_inits, (st_data_t)ftptr, NULL)) { + if (st_lookup(vm->static_ext_inits, (st_data_t)ftptr, NULL)) { *path = rb_filesystem_str_new_cstr(ftptr); RB_GC_GUARD(lookup_name); return 's'; @@ -1271,7 +1172,7 @@ search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func if (ft) goto feature_present; ftptr = RSTRING_PTR(tmp); - return rb_feature_p(vm_ns, ftptr, 0, FALSE, TRUE, 0); + return rb_feature_p(ns, ftptr, 0, FALSE, TRUE, 0); default: if (ft) { @@ -1280,7 +1181,7 @@ search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func /* fall through */ case loadable_ext_rb: ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (rb_feature_p(vm_ns, ftptr, ext, type == loadable_ext_rb, TRUE, &loading) && !loading) + if (rb_feature_p(ns, ftptr, ext, type == loadable_ext_rb, TRUE, &loading) && !loading) break; *path = tmp; } @@ -1301,9 +1202,9 @@ static VALUE load_ext(VALUE path, VALUE fname) { VALUE loaded = path; - GET_loading_vm_ns(); - if (NAMESPACE_USER_P(vm_ns->ns)) { - loaded = rb_namespace_local_extension(vm_ns->ns->ns_object, fname, path); + const rb_namespace_t *ns = rb_loading_namespace(); + if (NAMESPACE_USER_P(ns)) { + loaded = rb_namespace_local_extension(ns->ns_object, fname, path); } rb_scope_visibility_set(METHOD_VISI_PUBLIC); return (VALUE)dln_load_feature(RSTRING_PTR(loaded), RSTRING_PTR(fname)); @@ -1323,7 +1224,7 @@ run_static_ext_init(rb_vm_t *vm, const char *feature) } static int -no_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int expanded, const char **fn) +no_feature_p(const rb_namespace_t *ns, const char *feature, const char *ext, int rb, int expanded, const char **fn) { return 0; } @@ -1335,11 +1236,11 @@ rb_resolve_feature_path(VALUE klass, VALUE fname) VALUE path; int found; VALUE sym; - GET_loading_vm_ns(); + const rb_namespace_t *ns = rb_loading_namespace(); fname = rb_get_path(fname); path = rb_str_encode_ospath(fname); - found = search_required(vm_ns, path, &path, no_feature_p); + found = search_required(ns, path, &path, no_feature_p); switch (found) { case 'r': @@ -1374,21 +1275,6 @@ rb_ext_ractor_safe(bool flag) GET_THREAD()->ext_config.ractor_safe = flag; } -struct rb_vm_call_cfunc2_data { - VALUE recv; - VALUE arg1; - VALUE arg2; - VALUE block_handler; - VALUE filename; -}; - -static VALUE -call_load_ext_in_ns(VALUE data) -{ - struct rb_vm_call_cfunc2_data *arg = (struct rb_vm_call_cfunc2_data *)data; - return rb_vm_call_cfunc2(arg->recv, load_ext, arg->arg1, arg->arg2, arg->block_handler, arg->filename); -} - /* * returns * 0: if already loaded (false) @@ -1408,14 +1294,14 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa th->top_wrapper, th->top_self, ec->errinfo, ec, }; - GET_loading_vm_ns(); + const rb_namespace_t *ns = rb_loading_namespace(); enum ruby_tag_type state; char *volatile ftptr = 0; VALUE path; volatile VALUE saved_path; volatile VALUE realpath = 0; - VALUE realpaths = get_loaded_features_realpaths(vm_ns); - VALUE realpath_map = get_loaded_features_realpath_map(vm_ns); + VALUE realpaths = ns->loaded_features_realpaths; + VALUE realpath_map = ns->loaded_features_realpath_map; volatile bool reset_ext_config = false; volatile struct rb_ext_config prev_ext_config; @@ -1431,12 +1317,12 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa int found; RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname)); - found = search_required(vm_ns, path, &saved_path, rb_feature_p); + found = search_required(ns, path, &saved_path, rb_feature_p); RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname)); path = saved_path; if (found) { - if (!path || !(ftptr = load_lock(vm_ns, RSTRING_PTR(path), warn))) { + if (!path || !(ftptr = load_lock(ns, RSTRING_PTR(path), warn))) { result = 0; } else if (!*ftptr) { @@ -1453,10 +1339,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa switch (found) { case 'r': // iseq_eval_in_namespace will be called with the loading namespace eventually - if (NAMESPACE_OPTIONAL_P(vm_ns->ns)) { + if (NAMESPACE_OPTIONAL_P(ns)) { // check with NAMESPACE_OPTIONAL_P (not NAMESPACE_USER_P) for NS1::xxx naming // it is not expected for the main namespace - load_wrapping(saved.ec, path, vm_ns->ns->ns_object); + load_wrapping(saved.ec, path, ns->ns_object); } else { load_iseq_eval(saved.ec, path); @@ -1464,19 +1350,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa break; case 's': - // the loading namespace must be set to the current namespace before calling load_ext reset_ext_config = true; ext_config_push(th, &prev_ext_config); - struct rb_vm_call_cfunc2_data arg = { - .recv = rb_vm_top_self(), - .arg1 = path, - .arg2 = fname, - .block_handler = VM_BLOCK_HANDLER_NONE, - .filename = path, - }; - handle = rb_namespace_exec(vm_ns->ns, call_load_ext_in_ns, (VALUE)&arg); - rb_hash_aset(CURRENT_NS_RUBY_DLN_LIBMAP(vm_ns, ruby_dln_libmap), path, - SVALUE2NUM((SIGNED_VALUE)handle)); + handle = rb_vm_call_cfunc_in_namespace(ns->top_self, load_ext, path, fname, path, ns); + rb_hash_aset(ns->ruby_dln_libmap, path, SVALUE2NUM((SIGNED_VALUE)handle)); break; } result = TAG_RETURN; @@ -1492,7 +1369,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa if (reset_ext_config) ext_config_pop(th2, &prev_ext_config); path = saved_path; - if (ftptr) load_unlock(vm_ns, RSTRING_PTR(path), !state); + if (ftptr) load_unlock(ns, RSTRING_PTR(path), !state); if (state) { if (state == TAG_FATAL || state == TAG_THROW) { @@ -1518,7 +1395,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa } if (result == TAG_RETURN) { - rb_provide_feature(vm_ns, path); + rb_provide_feature(ns, path); VALUE real = realpath; if (real) { real = rb_fstring(real); @@ -1621,15 +1498,16 @@ void ruby_init_ext(const char *name, void (*init)(void)) { st_table *inits_table; - GET_loading_vm_ns(); + rb_vm_t *vm = GET_VM(); + const rb_namespace_t *ns = rb_loading_namespace(); - if (feature_provided(vm_ns, name, 0)) + if (feature_provided((rb_namespace_t *)ns, name, 0)) return; - inits_table = vm_ns->vm->static_ext_inits; + inits_table = vm->static_ext_inits; if (!inits_table) { inits_table = st_init_strtable(); - vm_ns->vm->static_ext_inits = inits_table; + vm->static_ext_inits = inits_table; } st_update(inits_table, (st_data_t)name, register_init_ext, (st_data_t)init); } @@ -1774,7 +1652,7 @@ rb_ext_resolve_symbol(const char* fname, const char* symbol) VALUE path; char *ext; VALUE fname_str = rb_str_new_cstr(fname); - GET_loading_vm_ns(); + const rb_namespace_t *ns = rb_loading_namespace(); resolved = rb_resolve_feature_path((VALUE)NULL, fname_str); if (NIL_P(resolved)) { @@ -1782,7 +1660,7 @@ rb_ext_resolve_symbol(const char* fname, const char* symbol) if (!ext || !IS_SOEXT(ext)) { rb_str_cat_cstr(fname_str, ".so"); } - if (rb_feature_p(vm_ns, fname, 0, FALSE, FALSE, 0)) { + if (rb_feature_p(ns, fname, 0, FALSE, FALSE, 0)) { return dln_symbol(NULL, symbol); } return NULL; @@ -1791,7 +1669,7 @@ rb_ext_resolve_symbol(const char* fname, const char* symbol) return NULL; } path = rb_ary_entry(resolved, 1); - handle = rb_hash_lookup(CURRENT_NS_RUBY_DLN_LIBMAP(vm_ns, ruby_dln_libmap), path); + handle = rb_hash_lookup(ns->ruby_dln_libmap, path); if (NIL_P(handle)) { return NULL; } @@ -1801,31 +1679,18 @@ rb_ext_resolve_symbol(const char* fname, const char* symbol) void Init_load(void) { - rb_vm_t *vm = GET_VM(); static const char var_load_path[] = "$:"; ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1); - rb_define_hooked_variable(var_load_path, (VALUE*)vm, load_path_getter, rb_gvar_readonly_setter); + rb_define_hooked_variable(var_load_path, 0, load_path_getter, rb_gvar_readonly_setter); rb_gvar_namespace_ready(var_load_path); rb_alias_variable(rb_intern_const("$-I"), id_load_path); rb_alias_variable(rb_intern_const("$LOAD_PATH"), id_load_path); - vm->load_path = rb_ary_new(); - vm->expanded_load_path = rb_ary_hidden_new(0); - vm->load_path_snapshot = rb_ary_hidden_new(0); - vm->load_path_check_cache = 0; - rb_define_singleton_method(vm->load_path, "resolve_feature_path", rb_resolve_feature_path, 1); rb_define_virtual_variable("$\"", get_LOADED_FEATURES, 0); rb_gvar_namespace_ready("$\""); rb_define_virtual_variable("$LOADED_FEATURES", get_LOADED_FEATURES, 0); // TODO: rb_alias_variable ? rb_gvar_namespace_ready("$LOADED_FEATURES"); - vm->loaded_features = rb_ary_new(); - vm->loaded_features_snapshot = rb_ary_hidden_new(0); - vm->loaded_features_index = st_init_numtable(); - vm->loaded_features_realpaths = rb_hash_new(); - rb_obj_hide(vm->loaded_features_realpaths); - vm->loaded_features_realpath_map = rb_hash_new(); - rb_obj_hide(vm->loaded_features_realpath_map); rb_define_global_function("load", rb_f_load, -1); rb_define_global_function("require", rb_f_require, 1); @@ -1834,7 +1699,4 @@ Init_load(void) rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, -1); rb_define_global_function("autoload", rb_f_autoload, 2); rb_define_global_function("autoload?", rb_f_autoload_p, -1); - - ruby_dln_libmap = rb_hash_new_with_size(0); - rb_vm_register_global_object(ruby_dln_libmap); } diff --git a/mini_builtin.c b/mini_builtin.c index d9827e70e472b8..3ff3dc5c1d2a08 100644 --- a/mini_builtin.c +++ b/mini_builtin.c @@ -96,24 +96,9 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta return iseq; } -static void -load_with_builtin_functions(const char *feature_name, const struct rb_builtin_function *table) -{ - const rb_iseq_t *iseq = builtin_iseq_load(feature_name, table); - rb_namespace_enable_builtin(); - rb_iseq_eval_with_refinement(iseq, rb_mNamespaceRefiner); - rb_namespace_disable_builtin(); -} - void rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin_function *table) { - const rb_iseq_t *iseq; - if (rb_namespace_available() && rb_mNamespaceRefiner) { - load_with_builtin_functions(feature_name, table); - } - else { - iseq = builtin_iseq_load(feature_name, table); - rb_iseq_eval(iseq); - } + const rb_iseq_t *iseq = builtin_iseq_load(feature_name, table); + rb_iseq_eval(iseq, rb_root_namespace()); } diff --git a/namespace.c b/namespace.c index 28e2c63a81940e..d3ac255363822f 100644 --- a/namespace.c +++ b/namespace.c @@ -1,5 +1,6 @@ /* indent-tabs-mode: nil */ +#include "eval_intern.h" #include "internal.h" #include "internal/class.h" #include "internal/eval.h" @@ -19,18 +20,13 @@ VALUE rb_cNamespace = 0; VALUE rb_cNamespaceEntry = 0; -VALUE rb_mNamespaceRefiner = 0; VALUE rb_mNamespaceLoader = 0; -static rb_namespace_t builtin_namespace_data = { - .ns_object = Qnil, - .ns_id = 0, - .is_builtin = true, - .is_user = false, - .is_optional = false +static rb_namespace_t root_namespace_data = { + /* Initialize values lazily in Init_namespace() */ }; -static rb_namespace_t * const root_namespace = 0; -static rb_namespace_t * const builtin_namespace = &builtin_namespace_data; + +static rb_namespace_t * root_namespace = &root_namespace_data; static rb_namespace_t * main_namespace = 0; static char *tmp_dir; static bool tmp_dir_has_dirsep; @@ -52,8 +48,6 @@ bool ruby_namespace_init_done = false; // extern VALUE rb_resolve_feature_path(VALUE klass, VALUE fname); static VALUE rb_namespace_inspect(VALUE obj); -static void namespace_push(rb_thread_t *th, VALUE namespace); -static VALUE namespace_pop(VALUE th_value); void rb_namespace_init_done(void) @@ -61,72 +55,29 @@ rb_namespace_init_done(void) ruby_namespace_init_done = true; } -void -rb_namespace_enable_builtin(void) -{ - VALUE require_stack = GET_VM()->require_stack; - if (require_stack) { - rb_ary_push(require_stack, Qnil); - } -} - -void -rb_namespace_disable_builtin(void) -{ - VALUE require_stack = GET_VM()->require_stack; - if (require_stack) { - rb_ary_pop(require_stack); - } -} - -void -rb_namespace_push_loading_namespace(const rb_namespace_t *ns) -{ - VALUE require_stack = GET_VM()->require_stack; - rb_ary_push(require_stack, ns->ns_object); -} - -void -rb_namespace_pop_loading_namespace(const rb_namespace_t *ns) -{ - VALUE require_stack = GET_VM()->require_stack; - long size = RARRAY_LEN(require_stack); - if (size == 0) - rb_bug("popping on the empty require_stack"); - VALUE latest = RARRAY_AREF(require_stack, size-1); - if (latest != ns->ns_object) - rb_bug("Inconsistent loading namespace"); - rb_ary_pop(require_stack); -} - -rb_namespace_t * +const rb_namespace_t * rb_root_namespace(void) { return root_namespace; } const rb_namespace_t * -rb_builtin_namespace(void) -{ - return (const rb_namespace_t *)builtin_namespace; -} - -rb_namespace_t * rb_main_namespace(void) { return main_namespace; } +/* static bool namespace_ignore_builtin_primitive_methods_p(const rb_namespace_t *ns, rb_method_definition_t *def) { if (!NAMESPACE_BUILTIN_P(ns)) { return false; } - /* Primitive methods (just to call C methods) covers/hides the effective + / Primitive methods (just to call C methods) covers/hides the effective namespaces, so ignore the methods' namespaces to expose user code's namespace to the implementation. - */ + / if (def->type == VM_METHOD_TYPE_ISEQ) { ID mid = def->original_id; const char *path = RSTRING_PTR(pathobj_path(def->body.iseq.iseqptr->body->location.pathobj)); @@ -169,10 +120,10 @@ block_proc_namespace(const VALUE procval) static const rb_namespace_t * current_namespace(bool permit_calling_builtin) { - /* + / * TODO: move this code to vm.c or somewhere else * when it's fully updated with VM_FRAME_FLAG_* - */ + / const rb_callable_method_entry_t *cme; const rb_namespace_t *ns; rb_execution_context_t *ec = GET_EC(); @@ -227,16 +178,28 @@ current_namespace(bool permit_calling_builtin) } return main_namespace; } +*/ const rb_namespace_t * rb_current_namespace(void) { - return current_namespace(true); + /* + * If RUBY_NAMESPACE is not set, the root namespace is the only available one. + * + * Until the main_namespace is not initialized, the root namespace is + * the only valid namespace. + * This early return is to avoid accessing EC before its setup. + */ + if (!main_namespace) + return root_namespace; + + return rb_vm_current_namespace(GET_EC()); } const rb_namespace_t * rb_loading_namespace(void) { + /* VALUE namespace; long len; VALUE require_stack = GET_VM()->require_stack; @@ -256,16 +219,11 @@ rb_loading_namespace(void) namespace = RARRAY_AREF(require_stack, len-1); return rb_get_namespace_t(namespace); -} - -const rb_namespace_t * -rb_definition_namespace(void) -{ - const rb_namespace_t *ns = current_namespace(true); - if (NAMESPACE_BUILTIN_P(ns)) { + */ + if (!main_namespace) return root_namespace; - } - return ns; + + return rb_vm_loading_namespace(GET_EC()); } static long namespace_id_counter = 0; @@ -283,37 +241,43 @@ namespace_generate_id(void) static void namespace_entry_initialize(rb_namespace_t *ns) { - rb_vm_t *vm = GET_VM(); + const rb_namespace_t *root = rb_root_namespace(); // These will be updated immediately ns->ns_object = 0; ns->ns_id = 0; - ns->top_self = 0; - ns->load_path = rb_ary_dup(vm->load_path); - ns->expanded_load_path = rb_ary_dup(vm->expanded_load_path); + ns->top_self = rb_obj_alloc(rb_cObject); + // TODO: + // rb_define_singleton_method(rb_vm_top_self(), "to_s", main_to_s, 0); + // rb_define_alias(rb_singleton_class(rb_vm_top_self()), "inspect", "to_s"); + ns->load_path = rb_ary_dup(root->load_path); + ns->expanded_load_path = rb_ary_dup(root->expanded_load_path); ns->load_path_snapshot = rb_ary_new(); ns->load_path_check_cache = 0; - ns->loaded_features = rb_ary_dup(vm->loaded_features); + ns->loaded_features = rb_ary_dup(root->loaded_features); ns->loaded_features_snapshot = rb_ary_new(); ns->loaded_features_index = st_init_numtable(); - ns->loaded_features_realpaths = rb_hash_dup(vm->loaded_features_realpaths); - ns->loaded_features_realpath_map = rb_hash_dup(vm->loaded_features_realpath_map); + ns->loaded_features_realpaths = rb_hash_dup(root->loaded_features_realpaths); + ns->loaded_features_realpath_map = rb_hash_dup(root->loaded_features_realpath_map); ns->loading_table = st_init_strtable(); ns->ruby_dln_libmap = rb_hash_new_with_size(0); ns->gvar_tbl = rb_hash_new_with_size(0); - ns->is_builtin = false; ns->is_user = true; ns->is_optional = true; } -void rb_namespace_gc_update_references(void *ptr) +void +rb_namespace_gc_update_references(void *ptr) { rb_namespace_t *ns = (rb_namespace_t *)ptr; - if (!NIL_P(ns->ns_object)) + if (!ns) return; + + if (ns->ns_object) ns->ns_object = rb_gc_location(ns->ns_object); - ns->top_self = rb_gc_location(ns->top_self); + if (ns->top_self) + ns->top_self = rb_gc_location(ns->top_self); ns->load_path = rb_gc_location(ns->load_path); ns->expanded_load_path = rb_gc_location(ns->expanded_load_path); ns->load_path_snapshot = rb_gc_location(ns->load_path_snapshot); @@ -332,6 +296,8 @@ void rb_namespace_entry_mark(void *ptr) { const rb_namespace_t *ns = (rb_namespace_t *)ptr; + if (!ns) return; + rb_gc_mark(ns->ns_object); rb_gc_mark(ns->top_self); rb_gc_mark(ns->load_path); @@ -349,11 +315,26 @@ rb_namespace_entry_mark(void *ptr) rb_gc_mark(ns->gvar_tbl); } +// TODO: implemente namespace_entry_free to free loading_table etc +/* +static int +free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg) +{ + xfree((char *)key); + return ST_DELETE; +} + if (vm->loading_table) { + st_foreach(vm->loading_table, free_loading_table_entry, 0); + st_free_table(vm->loading_table); + vm->loading_table = 0; + } +*/ #define namespace_entry_free RUBY_TYPED_DEFAULT_FREE static size_t namespace_entry_memsize(const void *ptr) { + // TODO: rb_st_memsize(loaded_features_index) + rb_st_memsize(vm->loading_table) return sizeof(rb_namespace_t); } @@ -391,10 +372,12 @@ rb_get_namespace_t(VALUE namespace) VALUE entry; ID id_namespace_entry; - if (!namespace) - return root_namespace; + VM_ASSERT(namespace); + if (NIL_P(namespace)) - return builtin_namespace; + return root_namespace; + + VM_ASSERT(NAMESPACE_OBJ_P(namespace)); CONST_ID(id_namespace_entry, "__namespace_entry__"); entry = rb_attr_get(namespace, id_namespace_entry); @@ -404,13 +387,10 @@ rb_get_namespace_t(VALUE namespace) VALUE rb_get_namespace_object(rb_namespace_t *ns) { - if (!ns) // root namespace - return Qfalse; + VM_ASSERT(ns && ns->ns_object); return ns->ns_object; } -static void setup_pushing_loading_namespace(rb_namespace_t *ns); - /* * call-seq: * Namespace.new -> new_namespace @@ -435,8 +415,6 @@ namespace_initialize(VALUE namespace) ns->ns_object = namespace; ns->ns_id = namespace_generate_id(); - ns->load_path = rb_ary_dup(GET_VM()->load_path); - ns->is_user = true; rb_define_singleton_method(ns->load_path, "resolve_feature_path", rb_resolve_feature_path, 1); // Set the Namespace object unique/consistent from any namespaces to have just single @@ -451,8 +429,6 @@ namespace_initialize(VALUE namespace) rb_ivar_set(namespace, id_namespace_entry, entry); - setup_pushing_loading_namespace(ns); - return namespace; } @@ -480,13 +456,12 @@ static VALUE rb_namespace_current(VALUE klass) { const rb_namespace_t *ns = rb_current_namespace(); - if (NAMESPACE_USER_P(ns)) { - return ns->ns_object; - } - if (NAMESPACE_BUILTIN_P(ns)) { + + if (!rb_namespace_available()) return Qnil; - } - return Qfalse; + + VM_ASSERT(ns && ns->ns_object); + return ns->ns_object; } /* @@ -512,6 +487,7 @@ rb_namespace_s_is_builtin_p(VALUE namespace, VALUE klass) static VALUE rb_namespace_load_path(VALUE namespace) { + VM_ASSERT(NAMESPACE_OBJ_P(namespace)); return rb_get_namespace_t(namespace)->load_path; } @@ -791,108 +767,79 @@ rb_namespace_local_extension(VALUE namespace, VALUE fname, VALUE path) // At least for _WIN32, deleting extension files should be delayed until the namespace's destructor. // And it requires calling dlclose before deleting it. -static void -namespace_push(rb_thread_t *th, VALUE namespace) -{ - if (RTEST(th->namespaces)) { - rb_ary_push(th->namespaces, namespace); - } - else { - th->namespaces = rb_ary_new_from_args(1, namespace); - } - th->ns = rb_get_namespace_t(namespace); -} - -static VALUE -namespace_pop(VALUE th_value) -{ - VALUE upper_ns; - long stack_len; - rb_thread_t *th = (rb_thread_t *)th_value; - VALUE namespaces = th->namespaces; - if (!namespaces) { - rb_bug("Too many namespace pops"); - } - rb_ary_pop(namespaces); - stack_len = RARRAY_LEN(namespaces); - if (stack_len == 0) { - th->namespaces = 0; - th->ns = main_namespace; - } - else { - upper_ns = RARRAY_AREF(namespaces, stack_len-1); - th->ns = rb_get_namespace_t(upper_ns); - } - return Qnil; -} - -VALUE -rb_namespace_exec(const rb_namespace_t *ns, namespace_exec_func *func, VALUE arg) -{ - rb_thread_t *th = GET_THREAD(); - namespace_push(th, ns ? ns->ns_object : Qnil); - return rb_ensure(func, arg, namespace_pop, (VALUE)th); -} - -struct namespace_pop2_arg { - rb_thread_t *th; - rb_namespace_t *ns; -}; - -static VALUE -namespace_both_pop(VALUE arg) -{ - struct namespace_pop2_arg *data = (struct namespace_pop2_arg *)arg; - namespace_pop((VALUE) data->th); - rb_namespace_pop_loading_namespace(data->ns); - return Qnil; -} - static VALUE rb_namespace_load(int argc, VALUE *argv, VALUE namespace) { VALUE fname, wrap; - rb_thread_t *th = GET_THREAD(); - rb_namespace_t *ns = rb_get_namespace_t(namespace); - rb_scan_args(argc, argv, "11", &fname, &wrap); + rb_vm_frame_flag_set_ns_require(GET_EC()); + VALUE args = rb_ary_new_from_args(2, fname, wrap); - namespace_push(th, namespace); - rb_namespace_push_loading_namespace(ns); - struct namespace_pop2_arg arg = { - .th = th, - .ns = ns - }; - return rb_ensure(rb_load_entrypoint, args, namespace_both_pop, (VALUE)&arg); + return rb_load_entrypoint(args); } static VALUE rb_namespace_require(VALUE namespace, VALUE fname) { - rb_thread_t *th = GET_THREAD(); - rb_namespace_t *ns = rb_get_namespace_t(namespace); - namespace_push(th, namespace); - rb_namespace_push_loading_namespace(ns); - struct namespace_pop2_arg arg = { - .th = th, - .ns = ns - }; - return rb_ensure(rb_require_string, fname, namespace_both_pop, (VALUE)&arg); + rb_vm_frame_flag_set_ns_require(GET_EC()); + + return rb_require_string(fname); } static VALUE rb_namespace_require_relative(VALUE namespace, VALUE fname) { - rb_thread_t *th = GET_THREAD(); - rb_namespace_t *ns = rb_get_namespace_t(namespace); - namespace_push(th, namespace); - rb_namespace_push_loading_namespace(ns); - struct namespace_pop2_arg arg = { - .th = th, - .ns = ns - }; - return rb_ensure(rb_require_relative_entrypoint, fname, namespace_both_pop, (VALUE)&arg); + rb_vm_frame_flag_set_ns_require(GET_EC()); + + return rb_require_relative_entrypoint(fname); +} + +static void +initialize_root_namespace(void) +{ + VALUE root_namespace, entry; + ID id_namespace_entry; + rb_vm_t *vm = GET_VM(); + rb_namespace_t *root = (rb_namespace_t *)rb_root_namespace(); + + root->load_path = rb_ary_new(); + root->expanded_load_path = rb_ary_hidden_new(0); + root->load_path_snapshot = rb_ary_hidden_new(0); + root->load_path_check_cache = 0; + rb_define_singleton_method(root->load_path, "resolve_feature_path", rb_resolve_feature_path, 1); + + root->loaded_features = rb_ary_new(); + root->loaded_features_snapshot = rb_ary_hidden_new(0); + root->loaded_features_index = st_init_numtable(); + root->loaded_features_realpaths = rb_hash_new(); + rb_obj_hide(root->loaded_features_realpaths); + root->loaded_features_realpath_map = rb_hash_new(); + rb_obj_hide(root->loaded_features_realpath_map); + + root->ruby_dln_libmap = rb_hash_new_with_size(0); + root->gvar_tbl = rb_hash_new_with_size(0); + + vm->root_namespace = root; + + if (rb_namespace_available()) { + CONST_ID(id_namespace_entry, "__namespace_entry__"); + + root_namespace = rb_obj_alloc(rb_cNamespace); + rb_evict_ivars_to_hash(root_namespace); + RCLASS_SET_PRIME_CLASSEXT_WRITABLE(root_namespace, true); + RCLASS_SET_CONST_TBL(root_namespace, RCLASSEXT_CONST_TBL(RCLASS_EXT_PRIME(rb_cObject)), true); + + root->ns_id = namespace_generate_id(); + root->ns_object = root_namespace; + + entry = TypedData_Wrap_Struct(rb_cNamespaceEntry, &rb_namespace_data_type, root); + rb_ivar_set(root_namespace, id_namespace_entry, entry); + } + else { + root->ns_id = 1; + root->ns_object = Qnil; + } } static VALUE @@ -918,9 +865,10 @@ void rb_initialize_main_namespace(void) { rb_namespace_t *ns; - rb_vm_t *vm = GET_VM(); - rb_thread_t *th = GET_THREAD(); VALUE main_ns; + rb_vm_t *vm = GET_VM(); + + VM_ASSERT(rb_namespace_available()); if (!namespace_experimental_warned) { rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL, @@ -933,13 +881,12 @@ rb_initialize_main_namespace(void) ns = rb_get_namespace_t(main_ns); ns->ns_object = main_ns; ns->ns_id = namespace_generate_id(); - ns->is_builtin = false; ns->is_user = true; ns->is_optional = false; rb_const_set(rb_cNamespace, rb_intern("MAIN"), main_ns); - vm->main_namespace = th->ns = main_namespace = ns; + vm->main_namespace = main_namespace = ns; } static VALUE @@ -954,8 +901,8 @@ rb_namespace_inspect(VALUE obj) ns = rb_get_namespace_t(obj); r = rb_str_new_cstr("#ns_id), rb_intern("to_s"), 0)); - if (NAMESPACE_BUILTIN_P(ns)) { - rb_str_cat_cstr(r, ",builtin"); + if (NAMESPACE_ROOT_P(ns)) { + rb_str_cat_cstr(r, ",root"); } if (NAMESPACE_USER_P(ns)) { rb_str_cat_cstr(r, ",user"); @@ -970,106 +917,24 @@ rb_namespace_inspect(VALUE obj) return r; } -struct refiner_calling_super_data { - int argc; - VALUE *argv; -}; - static VALUE -namespace_builtin_refiner_calling_super(VALUE arg) +rb_namespace_loading_func(int argc, VALUE *argv, VALUE _self) { - struct refiner_calling_super_data *data = (struct refiner_calling_super_data *)arg; - return rb_call_super(data->argc, data->argv); -} - -static VALUE -namespace_builtin_refiner_loading_func_ensure(VALUE _) -{ - rb_vm_t *vm = GET_VM(); - if (!vm->require_stack) - rb_bug("require_stack is not ready but the namespace refiner is called"); - rb_namespace_disable_builtin(); - return Qnil; -} - -static VALUE -rb_namespace_builtin_refiner_loading_func(int argc, VALUE *argv, VALUE _self) -{ - rb_vm_t *vm = GET_VM(); - if (!vm->require_stack) - rb_bug("require_stack is not ready but the namespace refiner is called"); - rb_namespace_enable_builtin(); - // const rb_namespace_t *ns = rb_loading_namespace(); - // printf("N:current loading ns: %ld\n", ns->ns_id); - struct refiner_calling_super_data data = { - .argc = argc, - .argv = argv - }; - return rb_ensure(namespace_builtin_refiner_calling_super, (VALUE)&data, - namespace_builtin_refiner_loading_func_ensure, Qnil); + rb_vm_frame_flag_set_ns_require(GET_EC()); + return rb_call_super(argc, argv); } static void -setup_builtin_refinement(VALUE mod) -{ - struct rb_refinements_data data; - rb_refinement_setup(&data, mod, rb_mKernel); - rb_define_method(data.refinement, "require", rb_namespace_builtin_refiner_loading_func, -1); - rb_define_method(data.refinement, "require_relative", rb_namespace_builtin_refiner_loading_func, -1); - rb_define_method(data.refinement, "load", rb_namespace_builtin_refiner_loading_func, -1); -} - -static VALUE -namespace_user_loading_func_calling_super(VALUE arg) -{ - struct refiner_calling_super_data *data = (struct refiner_calling_super_data *)arg; - return rb_call_super(data->argc, data->argv); -} - -static VALUE -namespace_user_loading_func_ensure(VALUE arg) -{ - rb_namespace_t *ns = (rb_namespace_t *)arg; - rb_namespace_pop_loading_namespace(ns); - return Qnil; -} - -static VALUE -rb_namespace_user_loading_func(int argc, VALUE *argv, VALUE _self) -{ - const rb_namespace_t *ns; - rb_vm_t *vm = GET_VM(); - if (!vm->require_stack) - rb_bug("require_stack is not ready but require/load is called in user namespaces"); - ns = rb_current_namespace(); - VM_ASSERT(rb_namespace_available() || !ns); - rb_namespace_push_loading_namespace(ns); - struct refiner_calling_super_data data = { - .argc = argc, - .argv = argv - }; - return rb_ensure(namespace_user_loading_func_calling_super, (VALUE)&data, - namespace_user_loading_func_ensure, (VALUE)ns); -} - -static VALUE -setup_pushing_loading_namespace_include(VALUE mod) -{ - rb_include_module(rb_cObject, mod); - return Qnil; -} - -static void -setup_pushing_loading_namespace(rb_namespace_t *ns) +namespace_define_loader_method(const char *name) { - rb_namespace_exec(ns, setup_pushing_loading_namespace_include, rb_mNamespaceLoader); + rb_define_private_method(rb_mNamespaceLoader, name, rb_namespace_loading_func, -1); + rb_define_singleton_method(rb_mNamespaceLoader, name, rb_namespace_loading_func, -1); } -static void -namespace_define_loader_method(const char *name) +void +Init_root_namespace(void) { - rb_define_private_method(rb_mNamespaceLoader, name, rb_namespace_user_loading_func, -1); - rb_define_singleton_method(rb_mNamespaceLoader, name, rb_namespace_user_loading_func, -1); + root_namespace->loading_table = st_init_strtable(); } void @@ -1104,17 +969,16 @@ Init_Namespace(void) rb_cNamespaceEntry = rb_define_class_under(rb_cNamespace, "Entry", rb_cObject); rb_define_alloc_func(rb_cNamespaceEntry, rb_namespace_entry_alloc); - /* :nodoc: */ - rb_mNamespaceRefiner = rb_define_module_under(rb_cNamespace, "Refiner"); - if (rb_namespace_available()) { - setup_builtin_refinement(rb_mNamespaceRefiner); - } + initialize_root_namespace(); /* :nodoc: */ rb_mNamespaceLoader = rb_define_module_under(rb_cNamespace, "Loader"); namespace_define_loader_method("require"); namespace_define_loader_method("require_relative"); namespace_define_loader_method("load"); + if (rb_namespace_available()) { + rb_include_module(rb_cObject, rb_mNamespaceLoader); + } rb_define_singleton_method(rb_cNamespace, "enabled?", rb_namespace_s_getenabled, 0); rb_define_singleton_method(rb_cNamespace, "current", rb_namespace_current, 0); @@ -1127,7 +991,4 @@ Init_Namespace(void) rb_define_method(rb_cNamespace, "eval", rb_namespace_eval, 1); rb_define_method(rb_cNamespace, "inspect", rb_namespace_inspect, 0); - - rb_vm_t *vm = GET_VM(); - vm->require_stack = rb_ary_new(); } diff --git a/proc.c b/proc.c index 8f0eb0a898b711..9c1a7a7fff2916 100644 --- a/proc.c +++ b/proc.c @@ -683,7 +683,6 @@ cfunc_proc_new(VALUE klass, VALUE ifunc) { rb_proc_t *proc; cfunc_proc_t *sproc; - const rb_namespace_t *ns = rb_current_namespace(); VALUE procval = TypedData_Make_Struct(klass, cfunc_proc_t, &proc_data_type, sproc); VALUE *ep; @@ -698,7 +697,6 @@ cfunc_proc_new(VALUE klass, VALUE ifunc) /* self? */ RB_OBJ_WRITE(procval, &proc->block.as.captured.code.ifunc, ifunc); - proc->ns = ns; proc->is_lambda = TRUE; return procval; } diff --git a/ruby.c b/ruby.c index f64412a9cf0fd2..8c1fb5719bc02e 100644 --- a/ruby.c +++ b/ruby.c @@ -449,7 +449,7 @@ ruby_push_include(const char *path, VALUE (*filter)(VALUE)) { const char sep = PATH_SEP_CHAR; const char *p, *s; - VALUE load_path = GET_VM()->load_path; + VALUE load_path = rb_root_namespace()->load_path; #ifdef __CYGWIN__ char rubylib[FILENAME_MAX]; VALUE buf = 0; @@ -754,7 +754,7 @@ ruby_init_loadpath(void) rb_gc_register_address(&ruby_archlibdir_path); ruby_archlibdir_path = archlibdir; - load_path = GET_VM()->load_path; + load_path = rb_root_namespace()->load_path; ruby_push_include(getenv("RUBYLIB"), identical_path); @@ -2328,8 +2328,8 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) char fbuf[MAXPATHLEN]; int i = (int)proc_options(argc, argv, opt, 0); unsigned int dump = opt->dump & dump_exit_bits; - rb_vm_t *vm = GET_VM(); - const long loaded_before_enc = RARRAY_LEN(vm->loaded_features); + const rb_namespace_t *ns = rb_root_namespace(); + const long loaded_before_enc = RARRAY_LEN(ns->loaded_features); if (opt->dump & (DUMP_BIT(usage)|DUMP_BIT(help))) { const char *const progname = @@ -2477,7 +2477,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) rb_obj_freeze(opt->script_name); if (IF_UTF8_PATH(uenc != lenc, 1)) { long i; - VALUE load_path = vm->load_path; + VALUE load_path = ns->load_path; const ID id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK; int modifiable = FALSE; @@ -2500,11 +2500,11 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) RARRAY_ASET(load_path, i, path); } if (modifiable) { - rb_ary_replace(vm->load_path_snapshot, load_path); + rb_ary_replace(ns->load_path_snapshot, load_path); } } { - VALUE loaded_features = vm->loaded_features; + VALUE loaded_features = ns->loaded_features; bool modified = false; for (long i = loaded_before_enc; i < RARRAY_LEN(loaded_features); ++i) { VALUE path = RARRAY_AREF(loaded_features, i); @@ -2516,7 +2516,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) RARRAY_ASET(loaded_features, i, path); } if (modified) { - rb_ary_replace(vm->loaded_features_snapshot, loaded_features); + rb_ary_replace(ns->loaded_features_snapshot, loaded_features); } } diff --git a/variable.c b/variable.c index b1782a01c85c93..bb3811a81c5425 100644 --- a/variable.c +++ b/variable.c @@ -1012,6 +1012,7 @@ rb_gvar_set(ID id, VALUE val) RB_VM_LOCKING() { entry = rb_global_entry(id); + // TODO: consider root/main namespaces if (USE_NAMESPACE_GVAR_TBL(ns, entry)) { rb_hash_aset(ns->gvar_tbl, rb_id2sym(entry->id), val); retval = val; @@ -3170,43 +3171,6 @@ autoload_apply_constants(VALUE _arguments) return Qtrue; } -struct autoload_feature_require_data { - struct autoload_load_arguments *arguments; - VALUE receiver; - VALUE feature; -}; - -static VALUE -autoload_feature_require_in_builtin(VALUE arg) -{ - struct autoload_feature_require_data *data = (struct autoload_feature_require_data *)arg; - - VALUE result = rb_funcall(data->receiver, rb_intern("require"), 1, data->feature); - if (RTEST(result)) { - return rb_mutex_synchronize(autoload_mutex, autoload_apply_constants, (VALUE)data->arguments); - } - return Qnil; -} - -static VALUE -autoload_feature_require_ensure_in_builtin(VALUE _arg) -{ - /* - * The gccct should be cleared again after the rb_funcall() to remove - * the inconsistent cache entry against the current namespace. - */ - rb_gccct_clear_table(Qnil); - rb_namespace_disable_builtin(); - return Qnil; -} - -static VALUE -autoload_feature_require_in_builtin_wrap(VALUE arg) -{ - return rb_ensure(autoload_feature_require_in_builtin, arg, - autoload_feature_require_ensure_in_builtin, Qnil); -} - static VALUE autoload_feature_require(VALUE _arguments) { @@ -3220,26 +3184,16 @@ autoload_feature_require(VALUE _arguments) // We save this for later use in autoload_apply_constants: arguments->autoload_data = rb_check_typeddata(autoload_const->autoload_data_value, &autoload_data_type); - if (NIL_P(autoload_namespace)) { - rb_namespace_enable_builtin(); - /* - * Clear the global cc cache table because the require method can be different from the current - * namespace's one and it may cause inconsistent cc-cme states. - * For example, the assertion below may fail in gccct_method_search(); - * VM_ASSERT(vm_cc_check_cme(cc, rb_callable_method_entry(klass, mid))) - */ - rb_gccct_clear_table(Qnil); - struct autoload_feature_require_data data = { - .arguments = arguments, - .receiver = receiver, - .feature = arguments->autoload_data->feature, - }; - return rb_namespace_exec(rb_builtin_namespace(), autoload_feature_require_in_builtin_wrap, (VALUE)&data); - } - - if (RTEST(autoload_namespace) && NAMESPACE_OPTIONAL_P(rb_get_namespace_t(autoload_namespace))) { + if (rb_namespace_available() && NAMESPACE_OBJ_P(autoload_namespace)) receiver = autoload_namespace; - } + + /* + * Clear the global cc cache table because the require method can be different from the current + * namespace's one and it may cause inconsistent cc-cme states. + * For example, the assertion below may fail in gccct_method_search(); + * VM_ASSERT(vm_cc_check_cme(cc, rb_callable_method_entry(klass, mid))) + */ + rb_gccct_clear_table(Qnil); VALUE result = rb_funcall(receiver, rb_intern("require"), 1, arguments->autoload_data->feature); diff --git a/vm.c b/vm.c index 154a0ba9d16d04..3eb5d74e8390e1 100644 --- a/vm.c +++ b/vm.c @@ -118,7 +118,15 @@ PUREFUNC(static inline VALUE VM_CF_BLOCK_HANDLER(const rb_control_frame_t * cons static inline VALUE VM_CF_BLOCK_HANDLER(const rb_control_frame_t * const cfp) { - const VALUE *ep = VM_CF_LEP(cfp); + const VALUE *ep; + if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_TOP) { + VM_ASSERT(VM_ENV_LOCAL_P(cfp->ep)); + /* Never set black_handler for VM_FRAME_MAGIC_TOP + * and the specval is used for namespace (rb_namespace_t) in the case + */ + return VM_BLOCK_HANDLER_NONE; + } + ep = VM_CF_LEP(cfp); return VM_ENV_BLOCK_HANDLER(ep); } @@ -778,15 +786,16 @@ vm_stat(int argc, VALUE *argv, VALUE self) /* control stack frame */ static void -vm_set_top_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq) +vm_set_top_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_namespace_t *ns) { if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_TOP) { rb_raise(rb_eTypeError, "Not a toplevel InstructionSequence"); } /* for return */ - vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, rb_ec_thread_ptr(ec)->top_self, - VM_BLOCK_HANDLER_NONE, + vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, + ns ? ns->top_self : rb_ec_thread_ptr(ec)->top_self, + GC_GUARDED_PTR(ns), (VALUE)vm_cref_new_toplevel(ec), /* cref or me */ ISEQ_BODY(iseq)->iseq_encoded, ec->cfp->sp, ISEQ_BODY(iseq)->local_table_size, ISEQ_BODY(iseq)->stack_max); @@ -992,7 +1001,11 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co VM_FORCE_WRITE_SPECIAL_CONST(&ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(prev_cfp->ep)); } } + else if (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)) { + // block_handler is always VM_BLOCK_HANDLER_NONE in this case + } else { + VM_ASSERT(VM_ENV_LOCAL_P(ep) && !VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)); VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep); if (block_handler != VM_BLOCK_HANDLER_NONE) { @@ -1180,7 +1193,6 @@ vm_proc_create_from_captured(VALUE klass, { VALUE procval = rb_proc_alloc(klass); rb_proc_t *proc = RTYPEDDATA_DATA(procval); - const rb_namespace_t *ns = rb_current_namespace(); VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), captured->ep)); @@ -1190,7 +1202,6 @@ vm_proc_create_from_captured(VALUE klass, rb_vm_block_ep_update(procval, &proc->block, captured->ep); vm_block_type_set(&proc->block, block_type); - proc->ns = ns; proc->is_from_method = is_from_method; proc->is_lambda = is_lambda; @@ -1222,12 +1233,10 @@ proc_create(VALUE klass, const struct rb_block *block, int8_t is_from_method, in { VALUE procval = rb_proc_alloc(klass); rb_proc_t *proc = RTYPEDDATA_DATA(procval); - const rb_namespace_t *ns = rb_current_namespace(); VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), vm_block_ep(block))); rb_vm_block_copy(procval, &proc->block, block); vm_block_type_set(&proc->block, block->type); - proc->ns = ns; proc->is_from_method = is_from_method; proc->is_lambda = is_lambda; @@ -2910,24 +2919,11 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, V /* misc */ VALUE -rb_iseq_eval(const rb_iseq_t *iseq) -{ - rb_execution_context_t *ec = GET_EC(); - VALUE val; - vm_set_top_stack(ec, iseq); - // TODO: set the namespace frame like require/load - val = vm_exec(ec); - return val; -} - -VALUE -rb_iseq_eval_with_refinement(const rb_iseq_t *iseq, VALUE mod) +rb_iseq_eval(const rb_iseq_t *iseq, const rb_namespace_t *ns) { rb_execution_context_t *ec = GET_EC(); VALUE val; - vm_set_top_stack(ec, iseq); - rb_vm_using_module(mod); - // TODO: set the namespace frame like require/load + vm_set_top_stack(ec, iseq, ns); val = vm_exec(ec); return val; } @@ -2937,8 +2933,7 @@ rb_iseq_eval_main(const rb_iseq_t *iseq) { rb_execution_context_t *ec = GET_EC(); VALUE val; - vm_set_main_stack(ec, iseq); - // TODO: set the namespace frame like require/load + vm_set_main_stack(ec, iseq); // TODO: not need to set the namespace? val = vm_exec(ec); return val; } @@ -2978,10 +2973,11 @@ rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, rb_execution_context_t *ec = GET_EC(); const rb_control_frame_t *reg_cfp = ec->cfp; const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP); + const rb_namespace_t *ns = rb_current_namespace(); VALUE val; vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, - recv, block_handler, + recv, GC_GUARDED_PTR(ns), (VALUE)vm_cref_new_toplevel(ec), /* cref or me */ 0, reg_cfp->sp, 0, 0); @@ -2991,9 +2987,11 @@ rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, return val; } +/* namespace */ + VALUE -rb_vm_call_cfunc2(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg2, - VALUE block_handler, VALUE filename) +rb_vm_call_cfunc_in_namespace(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg2, + VALUE filename, const rb_namespace_t *ns) { rb_execution_context_t *ec = GET_EC(); const rb_control_frame_t *reg_cfp = ec->cfp; @@ -3001,7 +2999,7 @@ rb_vm_call_cfunc2(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg VALUE val; vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, - recv, block_handler, + recv, GC_GUARDED_PTR(ns), (VALUE)vm_cref_new_toplevel(ec), /* cref or me */ 0, reg_cfp->sp, 0, 0); @@ -3011,6 +3009,86 @@ rb_vm_call_cfunc2(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg return val; } +void +rb_vm_frame_flag_set_ns_require(const rb_execution_context_t *ec) +{ + VM_ASSERT(rb_namespace_available()); + VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_NS_REQUIRE); +} + +static const rb_namespace_t * +current_namespace_on_env(const VALUE *ep) +{ + rb_callable_method_entry_t *cme; + const rb_namespace_t *ns; + const VALUE *lep = VM_EP_LEP(ep); + VM_ASSERT(lep); + VM_ASSERT(rb_namespace_available()); + + if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_METHOD) || VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_CFUNC)) { + cme = check_method_entry(lep[VM_ENV_DATA_INDEX_ME_CREF], TRUE); + VM_ASSERT(cme); + VM_ASSERT(cme->def); + return cme->def->ns; + } + else if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_TOP) || VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_CLASS)) { + VM_ASSERT(VM_ENV_LOCAL_P(lep)); + return VM_ENV_NAMESPACE(lep); + } + else if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_DUMMY)) { + // No valid local ep found (just after process boot?) + // return the root namespace (the only valid namespace) until the main is initialized + ns = rb_main_namespace(); + if (ns) + return ns; + return rb_root_namespace(); + } + else { + rb_bug("BUG: Local ep without cme/namespace, flags: %08lX", (unsigned long)lep[VM_ENV_DATA_INDEX_FLAGS]); + } + UNREACHABLE_RETURN(0); +} + +const rb_namespace_t * +rb_vm_current_namespace(const rb_execution_context_t *ec) +{ + const rb_control_frame_t *cfp; + + if (!rb_namespace_available() || !ec) + return rb_root_namespace(); + + cfp = ec->cfp; + return current_namespace_on_env(cfp->ep); +} + +const rb_namespace_t * +rb_vm_loading_namespace(const rb_execution_context_t *ec) +{ + const rb_control_frame_t *cfp, *current_cfp, *end_cfp; + + if (!rb_namespace_available() || !ec) + return rb_root_namespace(); + + cfp = ec->cfp; + current_cfp = cfp; + end_cfp = RUBY_VM_END_CONTROL_FRAME(ec); + + while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) { + if (VM_FRAME_RUBYFRAME_P(cfp)) { + if (VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_NS_REQUIRE)) { + if (RTEST(cfp->self) && NAMESPACE_OBJ_P(cfp->self)) { + // Namespace#require (, require_relative, load) + return rb_get_namespace_t(cfp->self); + } + return current_namespace_on_env(cfp->ep); + } + } + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + } + // no require/load with explicit namespaces. + return current_namespace_on_env(current_cfp->ep); +} + /* vm */ void @@ -3021,22 +3099,13 @@ rb_vm_update_references(void *ptr) vm->self = rb_gc_location(vm->self); vm->mark_object_ary = rb_gc_location(vm->mark_object_ary); - vm->load_path = rb_gc_location(vm->load_path); - vm->load_path_snapshot = rb_gc_location(vm->load_path_snapshot); - - if (vm->load_path_check_cache) { - vm->load_path_check_cache = rb_gc_location(vm->load_path_check_cache); - } - - vm->expanded_load_path = rb_gc_location(vm->expanded_load_path); - vm->loaded_features = rb_gc_location(vm->loaded_features); - vm->loaded_features_snapshot = rb_gc_location(vm->loaded_features_snapshot); - vm->loaded_features_realpaths = rb_gc_location(vm->loaded_features_realpaths); - vm->loaded_features_realpath_map = rb_gc_location(vm->loaded_features_realpath_map); - vm->top_self = rb_gc_location(vm->top_self); - vm->require_stack = rb_gc_location(vm->require_stack); vm->orig_progname = rb_gc_location(vm->orig_progname); + if (vm->root_namespace) + rb_namespace_gc_update_references(vm->root_namespace); + if (vm->main_namespace) + rb_namespace_gc_update_references(vm->main_namespace); + rb_gc_update_values(RUBY_NSIG, vm->trap_list.cmd); if (vm->coverages) { @@ -3108,29 +3177,18 @@ rb_vm_mark(void *ptr) rb_gc_mark_movable(vm->self); + if (vm->root_namespace) { + rb_namespace_entry_mark(vm->root_namespace); + } if (vm->main_namespace) { - rb_namespace_entry_mark((void *)vm->main_namespace); + rb_namespace_entry_mark(vm->main_namespace); } rb_gc_mark_movable(vm->mark_object_ary); - rb_gc_mark_movable(vm->load_path); - rb_gc_mark_movable(vm->load_path_snapshot); - rb_gc_mark_movable(vm->load_path_check_cache); - rb_gc_mark_movable(vm->expanded_load_path); - rb_gc_mark_movable(vm->loaded_features); - rb_gc_mark_movable(vm->loaded_features_snapshot); - rb_gc_mark_movable(vm->loaded_features_realpaths); - rb_gc_mark_movable(vm->loaded_features_realpath_map); - rb_gc_mark_movable(vm->require_stack); - rb_gc_mark_movable(vm->top_self); rb_gc_mark_movable(vm->orig_progname); rb_gc_mark_movable(vm->coverages); rb_gc_mark_movable(vm->me2counter); - if (vm->loading_table) { - rb_mark_tbl(vm->loading_table); - } - rb_gc_mark_values(RUBY_NSIG, vm->trap_list.cmd); rb_id_table_foreach_values(vm->negative_cme_table, vm_mark_negative_cme, NULL); @@ -3165,14 +3223,6 @@ rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE cls, rb_vm_register_global_object(exc); } -static int -free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg) -{ - xfree((char *)key); - return ST_DELETE; -} - -void rb_free_loaded_features_index(rb_vm_t *vm); void rb_objspace_free_objects(void *objspace); int @@ -3194,7 +3244,6 @@ ruby_vm_destruct(rb_vm_t *vm) rb_free_vm_opt_tables(); rb_free_warning(); rb_free_rb_global_tbl(); - rb_free_loaded_features_index(vm); rb_id_table_free(vm->negative_cme_table); st_free_table(vm->overloaded_cme_table); @@ -3225,11 +3274,6 @@ ruby_vm_destruct(rb_vm_t *vm) rb_vm_living_threads_init(vm); ruby_vm_run_at_exit_hooks(vm); - if (vm->loading_table) { - st_foreach(vm->loading_table, free_loading_table_entry, 0); - st_free_table(vm->loading_table); - vm->loading_table = 0; - } if (vm->ci_table) { st_free_table(vm->ci_table); vm->ci_table = NULL; @@ -3329,8 +3373,6 @@ vm_memsize(const void *ptr) return ( sizeof(rb_vm_t) + - rb_st_memsize(vm->loaded_features_index) + - rb_st_memsize(vm->loading_table) + rb_vm_memsize_postponed_job_queue() + rb_vm_memsize_workqueue(&vm->workqueue) + vm_memsize_at_exit_list(vm->at_exit) + @@ -3599,8 +3641,6 @@ thread_mark(void *ptr) rb_gc_mark(th->pending_interrupt_mask_stack); rb_gc_mark(th->top_self); rb_gc_mark(th->top_wrapper); - rb_gc_mark(th->namespaces); - if (NAMESPACE_USER_P(th->ns)) rb_namespace_entry_mark(th->ns); if (th->root_fiber) rb_fiber_mark_self(th->root_fiber); RUBY_ASSERT(th->ec == rb_fiberptr_get_ec(th->ec->fiber_ptr)); @@ -3727,6 +3767,8 @@ rb_ec_clear_vm_stack(rb_execution_context_t *ec) static void th_init(rb_thread_t *th, VALUE self, rb_vm_t *vm) { + const rb_namespace_t *ns = rb_current_namespace(); + th->self = self; rb_threadptr_root_fiber_setup(th); @@ -3748,9 +3790,12 @@ th_init(rb_thread_t *th, VALUE self, rb_vm_t *vm) th->status = THREAD_RUNNABLE; th->last_status = Qnil; th->top_wrapper = 0; - th->top_self = vm->top_self; // 0 while self == 0 - th->namespaces = 0; - th->ns = 0; + if (ns->top_self) { + th->top_self = ns->top_self; + } + else { + th->top_self = 0; + } th->value = Qundef; th->ec->errinfo = Qnil; @@ -3780,16 +3825,10 @@ th_init(rb_thread_t *th, VALUE self, rb_vm_t *vm) VALUE rb_thread_alloc(VALUE klass) { - rb_namespace_t *ns; - rb_execution_context_t *ec = GET_EC(); VALUE self = thread_alloc(klass); rb_thread_t *target_th = rb_thread_ptr(self); target_th->ractor = GET_RACTOR(); th_init(target_th, self, target_th->vm = GET_VM()); - if ((ns = rb_ec_thread_ptr(ec)->ns) == 0) { - ns = rb_main_namespace(); - } - target_th->ns = ns; return self; } @@ -4336,8 +4375,6 @@ Init_VM(void) th->vm = vm; th->top_wrapper = 0; th->top_self = rb_vm_top_self(); - th->namespaces = 0; - th->ns = 0; rb_vm_register_global_object((VALUE)iseq); th->ec->cfp->iseq = iseq; @@ -4552,7 +4589,6 @@ Init_vm_objects(void) /* initialize mark object array, hash */ vm->mark_object_ary = pin_array_list_new(Qnil); - vm->loading_table = st_init_strtable(); vm->ci_table = st_init_table(&vm_ci_hashtype); vm->cc_refinement_table = rb_set_init_numtable(); } @@ -4588,17 +4624,20 @@ main_to_s(VALUE obj) VALUE rb_vm_top_self(void) { - return GET_VM()->top_self; + const rb_namespace_t *ns = rb_current_namespace(); + VM_ASSERT(ns); + VM_ASSERT(ns->top_self); + return ns->top_self; } void Init_top_self(void) { rb_vm_t *vm = GET_VM(); - - vm->top_self = rb_obj_alloc(rb_cObject); - rb_define_singleton_method(rb_vm_top_self(), "to_s", main_to_s, 0); - rb_define_alias(rb_singleton_class(rb_vm_top_self()), "inspect", "to_s"); + vm->root_namespace = (rb_namespace_t *)rb_root_namespace(); + vm->root_namespace->top_self = rb_obj_alloc(rb_cObject); + rb_define_singleton_method(vm->root_namespace->top_self, "to_s", main_to_s, 0); + rb_define_alias(rb_singleton_class(vm->root_namespace->top_self), "inspect", "to_s"); } VALUE * diff --git a/vm_core.h b/vm_core.h index 487a4020eec5cd..fab440f1fd5175 100644 --- a/vm_core.h +++ b/vm_core.h @@ -311,7 +311,6 @@ struct rb_calling_info { int argc; bool kw_splat; VALUE heap_argv; - const rb_namespace_t *proc_ns; }; #ifndef VM_ARGC_STACK_MAX @@ -755,20 +754,10 @@ typedef struct rb_vm_struct { const VALUE special_exceptions[ruby_special_error_count]; /* namespace */ + rb_namespace_t *root_namespace; rb_namespace_t *main_namespace; /* load */ - VALUE top_self; - VALUE load_path; - VALUE load_path_snapshot; - VALUE load_path_check_cache; - VALUE expanded_load_path; - VALUE loaded_features; - VALUE loaded_features_snapshot; - VALUE loaded_features_realpaths; - VALUE loaded_features_realpath_map; - struct st_table *loaded_features_index; - struct st_table *loading_table; // For running the init function of statically linked // extensions when they are loaded struct st_table *static_ext_inits; @@ -832,9 +821,6 @@ typedef struct rb_vm_struct { size_t fiber_vm_stack_size; size_t fiber_machine_stack_size; } default_params; - - // TODO: a single require_stack can't support multi-threaded require trees - VALUE require_stack; } rb_vm_t; /* default values */ @@ -1141,9 +1127,6 @@ typedef struct rb_thread_struct { /* for load(true) */ VALUE top_self; VALUE top_wrapper; - /* for namespace */ - VALUE namespaces; // Stack of namespaces - rb_namespace_t *ns; // The current one /* thread control */ @@ -1283,7 +1266,6 @@ RUBY_SYMBOL_EXPORT_END typedef struct { const struct rb_block block; - const rb_namespace_t *ns; unsigned int is_from_method: 1; /* bool */ unsigned int is_lambda: 1; /* bool */ unsigned int is_isolated: 1; /* bool */ @@ -1375,11 +1357,11 @@ typedef rb_control_frame_t * enum vm_frame_env_flags { /* Frame/Environment flag bits: - * MMMM MMMM MMMM MMMM __FF FFFF FFFE EEEX (LSB) + * MMMM MMMM MMMM MMMM ___F FFFF FFFE EEEX (LSB) * * X : tag for GC marking (It seems as Fixnum) * EEE : 4 bits Env flags - * FF..: 9 bits Frame flags + * FF..: 8 bits Frame flags * MM..: 15 bits frame magic (to check frame corruption) */ @@ -1402,10 +1384,9 @@ enum vm_frame_env_flags { VM_FRAME_FLAG_CFRAME = 0x0080, VM_FRAME_FLAG_LAMBDA = 0x0100, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM = 0x0200, - VM_FRAME_FLAG_CFRAME_KW = 0x0400, - VM_FRAME_FLAG_PASSED = 0x0800, - VM_FRAME_FLAG_NS_SWITCH = 0x1000, - VM_FRAME_FLAG_LOAD_ISEQ = 0x2000, + VM_FRAME_FLAG_CFRAME_KW = 0x0400, + VM_FRAME_FLAG_PASSED = 0x0800, + VM_FRAME_FLAG_NS_REQUIRE = 0x1000, /* env flag */ VM_ENV_FLAG_LOCAL = 0x0002, @@ -1456,6 +1437,12 @@ VM_ENV_FLAGS_UNCHECKED(const VALUE *ep, long flag) return flags & flag; } +static inline unsigned long +VM_ENV_FRAME_TYPE_P(const VALUE *ep, unsigned long frame_type) +{ + return VM_ENV_FLAGS(ep, VM_FRAME_MAGIC_MASK) == frame_type; +} + static inline unsigned long VM_FRAME_TYPE(const rb_control_frame_t *cfp) { @@ -1536,9 +1523,9 @@ VM_FRAME_RUBYFRAME_P_UNCHECKED(const rb_control_frame_t *cfp) } static inline int -VM_FRAME_NS_SWITCH_P(const rb_control_frame_t *cfp) +VM_FRAME_NS_REQUIRE_P(const rb_control_frame_t *cfp) { - return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_NS_SWITCH) != 0; + return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_NS_REQUIRE) != 0; } #define RUBYVM_CFUNC_FRAME_P(cfp) \ @@ -1575,10 +1562,23 @@ VM_ENV_PREV_EP(const VALUE *ep) static inline VALUE VM_ENV_BLOCK_HANDLER(const VALUE *ep) { + if (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CLASS) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)) { + VM_ASSERT(VM_ENV_LOCAL_P(ep)); + return VM_BLOCK_HANDLER_NONE; + } + VM_ASSERT(VM_ENV_LOCAL_P(ep)); return ep[VM_ENV_DATA_INDEX_SPECVAL]; } +static inline const rb_namespace_t * +VM_ENV_NAMESPACE(const VALUE *ep) +{ + VM_ASSERT(VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CLASS) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)); + VM_ASSERT(VM_ENV_LOCAL_P(ep)); + return (const rb_namespace_t *)GC_GUARDED_PTR_REF(ep[VM_ENV_DATA_INDEX_SPECVAL]); +} + #if VM_CHECK_MODE > 0 int rb_vm_ep_in_heap_p(const VALUE *ep); #endif @@ -1899,8 +1899,7 @@ NORETURN(void rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int /* functions about thread/vm execution */ RUBY_SYMBOL_EXPORT_BEGIN -VALUE rb_iseq_eval(const rb_iseq_t *iseq); -VALUE rb_iseq_eval_with_refinement(const rb_iseq_t *iseq, VALUE mod); +VALUE rb_iseq_eval(const rb_iseq_t *iseq, const rb_namespace_t *ns); VALUE rb_iseq_eval_main(const rb_iseq_t *iseq); VALUE rb_iseq_path(const rb_iseq_t *iseq); VALUE rb_iseq_realpath(const rb_iseq_t *iseq); diff --git a/vm_dump.c b/vm_dump.c index 131844b4cc1766..03f573a516fe61 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -1150,6 +1150,7 @@ rb_vm_bugreport(const void *ctx, FILE *errout) enum {other_runtime_info = 0}; #endif const rb_vm_t *const vm = GET_VM(); + const rb_namespace_t *ns = rb_current_namespace(); const rb_execution_context_t *ec = rb_current_execution_context(false); if (vm && ec) { @@ -1198,10 +1199,19 @@ rb_vm_bugreport(const void *ctx, FILE *errout) LIMITED_NAME_LENGTH(name), RSTRING_PTR(name)); kprintf("\n"); } - if (vm->loaded_features) { + if (rb_namespace_available()) { + kprintf("* Namespace: enabled\n"); + kprintf("* Current namespace id: %ld, type: %s\n", + ns->ns_id, + NAMESPACE_USER_P(ns) ? (NAMESPACE_MAIN_P(ns) ? "main" : "user") : "root"); + } + else { + kprintf("* Namespace: disabled\n"); + } + if (ns->loaded_features) { kprintf("* Loaded features:\n\n"); - for (i=0; iloaded_features); i++) { - name = RARRAY_AREF(vm->loaded_features, i); + for (i=0; iloaded_features); i++) { + name = RARRAY_AREF(ns->loaded_features, i); if (RB_TYPE_P(name, T_STRING)) { kprintf(" %4d %.*s\n", i, LIMITED_NAME_LENGTH(name), RSTRING_PTR(name)); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 75339aaa5c5abd..d50b11e377874b 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -5238,20 +5238,6 @@ block_proc_is_lambda(const VALUE procval) } } -static inline const rb_namespace_t * -block_proc_namespace(const VALUE procval) -{ - rb_proc_t *proc; - - if (procval) { - GetProcPtr(procval, proc); - return proc->ns; - } - else { - return NULL; - } -} - static VALUE vm_yield_with_cfunc(rb_execution_context_t *ec, const struct rb_captured_block *captured, @@ -5407,10 +5393,6 @@ vm_invoke_iseq_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, SET_SP(rsp); - if (calling->proc_ns) { - frame_flag |= VM_FRAME_FLAG_NS_SWITCH; - } - vm_push_frame(ec, iseq, frame_flag, captured->self, @@ -5511,9 +5493,6 @@ vm_invoke_proc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, { while (vm_block_handler_type(block_handler) == block_handler_type_proc) { VALUE proc = VM_BH_TO_PROC(block_handler); - if (!calling->proc_ns) { - calling->proc_ns = block_proc_namespace(proc); - } is_lambda = block_proc_is_lambda(proc); block_handler = vm_proc_to_block_handler(proc); } diff --git a/vm_insnhelper.h b/vm_insnhelper.h index 015edaed9d12ee..8e7b7c743e7ebb 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -144,7 +144,7 @@ CC_SET_FASTPATH(const struct rb_callcache *cc, vm_call_handler func, bool enable } } -#define GET_BLOCK_HANDLER() (GET_LEP()[VM_ENV_DATA_INDEX_SPECVAL]) +#define GET_BLOCK_HANDLER() VM_ENV_BLOCK_HANDLER(VM_EP_LEP(GET_EP())) /**********************************************************/ /* deal with control flow 3: exception */ From 76c4663a77796fdcba539250dca3e6786ca0fd32 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 22 Jun 2025 17:38:24 +0900 Subject: [PATCH 03/28] Fix Namespace.current to show its caller's namespace Calling rb_current_namespace() in rb_namespace_current() means to show the definition namespace of Namespace.current itself (it's the root always) but the users' expectation is to show the namespace of the place where the Namespace.current is called. --- eval_intern.h | 1 + namespace.c | 3 ++- vm.c | 18 +++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/eval_intern.h b/eval_intern.h index 6353319c6ffe83..4ac950e23813cb 100644 --- a/eval_intern.h +++ b/eval_intern.h @@ -299,6 +299,7 @@ VALUE rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, VALUE block_ VALUE rb_vm_call_cfunc_in_namespace(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg2, VALUE filename, const rb_namespace_t *ns); void rb_vm_frame_flag_set_ns_require(const rb_execution_context_t *ec); const rb_namespace_t *rb_vm_current_namespace(const rb_execution_context_t *ec); +const rb_namespace_t *rb_vm_caller_namespace(const rb_execution_context_t *ec); const rb_namespace_t *rb_vm_loading_namespace(const rb_execution_context_t *ec); void rb_vm_set_progname(VALUE filename); VALUE rb_vm_cbase(void); diff --git a/namespace.c b/namespace.c index d3ac255363822f..65a90cb0da405c 100644 --- a/namespace.c +++ b/namespace.c @@ -455,11 +455,12 @@ rb_namespace_s_getenabled(VALUE namespace) static VALUE rb_namespace_current(VALUE klass) { - const rb_namespace_t *ns = rb_current_namespace(); + const rb_namespace_t *ns; if (!rb_namespace_available()) return Qnil; + ns = rb_vm_caller_namespace(GET_EC()); VM_ASSERT(ns && ns->ns_object); return ns->ns_object; } diff --git a/vm.c b/vm.c index 3eb5d74e8390e1..83119c32d2fc6c 100644 --- a/vm.c +++ b/vm.c @@ -3052,13 +3052,21 @@ current_namespace_on_env(const VALUE *ep) const rb_namespace_t * rb_vm_current_namespace(const rb_execution_context_t *ec) { - const rb_control_frame_t *cfp; + VM_ASSERT(rb_namespace_available()); + return current_namespace_on_env(ec->cfp->ep); +} - if (!rb_namespace_available() || !ec) - return rb_root_namespace(); +const rb_namespace_t * +rb_vm_caller_namespace(const rb_execution_context_t *ec) +{ + const rb_control_frame_t *caller_cfp; - cfp = ec->cfp; - return current_namespace_on_env(cfp->ep); + VM_ASSERT(rb_namespace_available()); + + // The current control frame is MAGIC_CFUNC to call Namespace.current, but + // we want to get the current namespace of its caller. + caller_cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp)); + return current_namespace_on_env(caller_cfp->ep); } const rb_namespace_t * From 545cee083b9096366884bf0b092f064db6682d75 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 22 Jun 2025 18:02:01 +0900 Subject: [PATCH 04/28] There is no longer needs to evict ivars thanks to fields See 8b5ac5abf258270b32ef63a6acb4eb0d191f79d9 --- namespace.c | 1 - 1 file changed, 1 deletion(-) diff --git a/namespace.c b/namespace.c index 65a90cb0da405c..81017f902bfde5 100644 --- a/namespace.c +++ b/namespace.c @@ -827,7 +827,6 @@ initialize_root_namespace(void) CONST_ID(id_namespace_entry, "__namespace_entry__"); root_namespace = rb_obj_alloc(rb_cNamespace); - rb_evict_ivars_to_hash(root_namespace); RCLASS_SET_PRIME_CLASSEXT_WRITABLE(root_namespace, true); RCLASS_SET_CONST_TBL(root_namespace, RCLASSEXT_CONST_TBL(RCLASS_EXT_PRIME(rb_cObject)), true); From 2100826243ae23e159ccdf9c9805a84074261808 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 22 Jun 2025 18:03:57 +0900 Subject: [PATCH 05/28] Fix wrong way to check an object is an instance of rb_cNamespace --- internal/namespace.h | 2 +- namespace.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/namespace.h b/internal/namespace.h index c68f0987aa814f..9ffc9a5c8be8bb 100644 --- a/internal/namespace.h +++ b/internal/namespace.h @@ -40,7 +40,7 @@ struct rb_namespace_struct { }; typedef struct rb_namespace_struct rb_namespace_t; -#define NAMESPACE_OBJ_P(obj) (CLASS_OF(obj) == rb_cNamespace) +#define NAMESPACE_OBJ_P(obj) (rb_obj_class(obj) == rb_cNamespace) #define NAMESPACE_ROOT_P(ns) (ns && !ns->is_user) #define NAMESPACE_USER_P(ns) (ns && ns->is_user) diff --git a/namespace.c b/namespace.c index 81017f902bfde5..8d50ba441566aa 100644 --- a/namespace.c +++ b/namespace.c @@ -877,7 +877,8 @@ rb_initialize_main_namespace(void) namespace_experimental_warned = 1; } - main_ns = rb_class_new_instance_pass_kw(0, NULL, rb_cNamespace); + main_ns = rb_class_new_instance(0, NULL, rb_cNamespace); + VM_ASSERT(NAMESPACE_OBJ_P(main_ns)); ns = rb_get_namespace_t(main_ns); ns->ns_object = main_ns; ns->ns_id = namespace_generate_id(); From c755f35f0ef755274ba409e3c6e21b759f248b29 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 24 Jun 2025 23:11:01 +0900 Subject: [PATCH 06/28] Stop using ns->top_self here because it's set to th->top_self beforehand if needed --- vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm.c b/vm.c index 83119c32d2fc6c..76f356342b867e 100644 --- a/vm.c +++ b/vm.c @@ -794,7 +794,7 @@ vm_set_top_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_nam /* for return */ vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, - ns ? ns->top_self : rb_ec_thread_ptr(ec)->top_self, + rb_ec_thread_ptr(ec)->top_self, GC_GUARDED_PTR(ns), (VALUE)vm_cref_new_toplevel(ec), /* cref or me */ ISEQ_BODY(iseq)->iseq_encoded, ec->cfp->sp, From 48523daef68a9ad2361bb2ee281c90e940330ed2 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 29 Jun 2025 09:42:22 +0900 Subject: [PATCH 07/28] Follow the usual naming rule for singleton methods --- namespace.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/namespace.c b/namespace.c index 8d50ba441566aa..7fc45f168851c3 100644 --- a/namespace.c +++ b/namespace.c @@ -439,7 +439,7 @@ namespace_initialize(VALUE namespace) * Returns +true+ if namespace is enabled. */ static VALUE -rb_namespace_s_getenabled(VALUE namespace) +rb_namespace_s_getenabled(VALUE recv) { return RBOOL(rb_namespace_available()); } @@ -453,7 +453,7 @@ rb_namespace_s_getenabled(VALUE namespace) * Returns +false+ if namespace is not enabled. */ static VALUE -rb_namespace_current(VALUE klass) +rb_namespace_s_current(VALUE recv) { const rb_namespace_t *ns; @@ -472,7 +472,7 @@ rb_namespace_current(VALUE klass) * Returns +true+ if +klass+ is only in a user namespace. */ static VALUE -rb_namespace_s_is_builtin_p(VALUE namespace, VALUE klass) +rb_namespace_s_is_builtin_p(VALUE recv, VALUE klass) { if (RCLASS_PRIME_CLASSEXT_READABLE_P(klass) && !RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass)) return Qtrue; @@ -982,7 +982,7 @@ Init_Namespace(void) } rb_define_singleton_method(rb_cNamespace, "enabled?", rb_namespace_s_getenabled, 0); - rb_define_singleton_method(rb_cNamespace, "current", rb_namespace_current, 0); + rb_define_singleton_method(rb_cNamespace, "current", rb_namespace_s_current, 0); rb_define_singleton_method(rb_cNamespace, "is_builtin?", rb_namespace_s_is_builtin_p, 1); rb_define_method(rb_cNamespace, "load_path", rb_namespace_load_path, 0); From bb21b619f01926fa99f6cca5ec8ac89389207d10 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 29 Jun 2025 09:44:31 +0900 Subject: [PATCH 08/28] Detect the correct loading namespace from control frames * checking all control frames (instead of filtering by VM_FRAME_RUBYFRAME_P) because VM_FRAME_FLAG_NS_REQUIRE is set on non-rubyframe * skip frames of CFUNC in the root namespace for Kernel#require (etc) to avoid detecting the root namespace of those frames wrongly --- vm.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/vm.c b/vm.c index 76f356342b867e..790a5af4f0cf7d 100644 --- a/vm.c +++ b/vm.c @@ -3065,10 +3065,24 @@ rb_vm_caller_namespace(const rb_execution_context_t *ec) // The current control frame is MAGIC_CFUNC to call Namespace.current, but // we want to get the current namespace of its caller. - caller_cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp)); + caller_cfp = vm_get_ruby_level_caller_cfp(ec, ec->cfp); return current_namespace_on_env(caller_cfp->ep); } +static const rb_control_frame_t * +find_loader_control_frame(const rb_control_frame_t *cfp, const rb_control_frame_t *end_cfp) +{ + while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) { + if (!VM_ENV_FRAME_TYPE_P(cfp->ep, VM_FRAME_MAGIC_CFUNC)) + break; + if (!NAMESPACE_ROOT_P(current_namespace_on_env(cfp->ep))) + break; + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + } + VM_ASSERT(RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)); + return cfp; +} + const rb_namespace_t * rb_vm_loading_namespace(const rb_execution_context_t *ec) { @@ -3082,14 +3096,14 @@ rb_vm_loading_namespace(const rb_execution_context_t *ec) end_cfp = RUBY_VM_END_CONTROL_FRAME(ec); while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) { - if (VM_FRAME_RUBYFRAME_P(cfp)) { - if (VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_NS_REQUIRE)) { - if (RTEST(cfp->self) && NAMESPACE_OBJ_P(cfp->self)) { - // Namespace#require (, require_relative, load) - return rb_get_namespace_t(cfp->self); - } - return current_namespace_on_env(cfp->ep); + if (VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_NS_REQUIRE)) { + if (RTEST(cfp->self) && NAMESPACE_OBJ_P(cfp->self)) { + // Namespace#require, #require_relative, #load + return rb_get_namespace_t(cfp->self); } + // Kernel#require, #require_relative, #load + cfp = find_loader_control_frame(cfp, end_cfp); + return current_namespace_on_env(cfp->ep); } cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); } From a5df24fe010d3631b324a6aadcb2db5b32c270e5 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 27 Jul 2025 11:02:52 +0900 Subject: [PATCH 09/28] Define a debug method Kernel#dump_classext only when RUBY_DEBUG is set --- namespace.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/namespace.c b/namespace.c index 7fc45f168851c3..e30d426d68b237 100644 --- a/namespace.c +++ b/namespace.c @@ -950,6 +950,140 @@ Init_enable_namespace(void) } } +#ifdef RUBY_DEBUG + +static const char * +classname(VALUE klass) +{ + VALUE p = RCLASS_CLASSPATH(klass); + if (RTEST(p)) + return RSTRING_PTR(p); + if (RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS)) + return RSTRING_PTR(rb_inspect(klass)); + return "NonClassValue"; +} + +static enum rb_id_table_iterator_result +dump_classext_methods_i(ID mid, VALUE _val, void *data) +{ + VALUE ary = (VALUE)data; + rb_ary_push(ary, rb_id2str(mid)); + return ID_TABLE_CONTINUE; +} + +static enum rb_id_table_iterator_result +dump_classext_constants_i(ID mid, VALUE _val, void *data) +{ + VALUE ary = (VALUE)data; + rb_ary_push(ary, rb_id2str(mid)); + return ID_TABLE_CONTINUE; +} + +static void +dump_classext_i(rb_classext_t *ext, bool is_prime, VALUE _ns, void *data) +{ + char buf[4096]; + struct rb_id_table *tbl; + VALUE ary, res = (VALUE)data; + + snprintf(buf, 4096, "Namespace %ld:%s classext %p\n", + RCLASSEXT_NS(ext)->ns_id, is_prime ? " prime" : "", (void *)ext); + rb_str_cat_cstr(res, buf); + + snprintf(buf, 2048, " Super: %s\n", classname(RCLASSEXT_SUPER(ext))); + rb_str_cat_cstr(res, buf); + + tbl = RCLASSEXT_M_TBL(ext); + if (tbl) { + ary = rb_ary_new_capa((long)rb_id_table_size(tbl)); + rb_id_table_foreach(RCLASSEXT_M_TBL(ext), dump_classext_methods_i, (void *)ary); + rb_ary_sort_bang(ary); + snprintf(buf, 4096, " Methods(%ld): ", RARRAY_LEN(ary)); + rb_str_cat_cstr(res, buf); + rb_str_concat(res, rb_ary_join(ary, rb_str_new_cstr(","))); + rb_str_cat_cstr(res, "\n"); + } + else { + rb_str_cat_cstr(res, " Methods(0): .\n"); + } + + tbl = RCLASSEXT_CONST_TBL(ext); + if (tbl) { + ary = rb_ary_new_capa((long)rb_id_table_size(tbl)); + rb_id_table_foreach(tbl, dump_classext_constants_i, (void *)ary); + rb_ary_sort_bang(ary); + snprintf(buf, 4096, " Constants(%ld): ", RARRAY_LEN(ary)); + rb_str_cat_cstr(res, buf); + rb_str_concat(res, rb_ary_join(ary, rb_str_new_cstr(","))); + rb_str_cat_cstr(res, "\n"); + } + else { + rb_str_cat_cstr(res, " Constants(0): .\n"); + } +} + +static VALUE +rb_f_dump_classext(VALUE recv, VALUE klass) +{ + /* + * The desired output String value is: + * Class: 0x88800932 (String) [singleton] + * Prime classext namespace(2,main), readable(t), writable(f) + * Non-prime classexts: 3 + * Namespace 2: prime classext 0x88800933 + * Super: Object + * Methods(43): aaaaa, bbbb, cccc, dddd, eeeee, ffff, gggg, hhhhh, ... + * Constants(12): FOO, Bar, ... + * Namespace 5: classext 0x88800934 + * Super: Object + * Methods(43): aaaaa, bbbb, cccc, dddd, eeeee, ffff, gggg, hhhhh, ... + * Constants(12): FOO, Bar, ... + */ + char buf[2048]; + VALUE res; + const rb_classext_t *ext; + const rb_namespace_t *ns; + st_table *classext_tbl; + + if (!(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE))) { + snprintf(buf, 2048, "Non-class/module value: %p (%s)\n", (void *)klass, rb_type_str(BUILTIN_TYPE(klass))); + return rb_str_new_cstr(buf); + } + + if (RB_TYPE_P(klass, T_CLASS)) { + snprintf(buf, 2048, "Class: %p (%s)%s\n", + (void *)klass, classname(klass), RCLASS_SINGLETON_P(klass) ? " [singleton]" : ""); + } + else { + snprintf(buf, 2048, "Module: %p (%s)\n", (void *)klass, classname(klass)); + } + res = rb_str_new_cstr(buf); + + ext = RCLASS_EXT_PRIME(klass); + ns = RCLASSEXT_NS(ext); + snprintf(buf, 2048, "Prime classext namespace(%ld,%s), readable(%s), writable(%s)\n", + ns->ns_id, + NAMESPACE_ROOT_P(ns) ? "root" : (NAMESPACE_MAIN_P(ns) ? "main" : "optional"), + RCLASS_PRIME_CLASSEXT_READABLE_P(klass) ? "t" : "f", + RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass) ? "t" : "f"); + rb_str_cat_cstr(res, buf); + + classext_tbl = RCLASS_CLASSEXT_TBL(klass); + if (!classext_tbl) { + rb_str_cat_cstr(res, "Non-prime classexts: 0\n"); + } + else { + snprintf(buf, 2048, "Non-prime classexts: %zu\n", st_table_size(classext_tbl)); + rb_str_cat_cstr(res, buf); + } + + rb_class_classext_foreach(klass, dump_classext_i, (void *)res); + + return res; +} + +#endif /* RUBY_DEBUG */ + /* * Document-class: Namespace * @@ -977,8 +1111,13 @@ Init_Namespace(void) namespace_define_loader_method("require"); namespace_define_loader_method("require_relative"); namespace_define_loader_method("load"); + if (rb_namespace_available()) { rb_include_module(rb_cObject, rb_mNamespaceLoader); + +#ifdef RUBY_DEBUG + rb_define_global_function("dump_classext", rb_f_dump_classext, 1); +#endif } rb_define_singleton_method(rb_cNamespace, "enabled?", rb_namespace_s_getenabled, 0); From 20c73b17232cc5bd0f8a3c13507d56b5f11ab2ed Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 27 Jul 2025 11:04:15 +0900 Subject: [PATCH 10/28] Skip CFUNC frames in the current namespace detection * The current namespace should be based on the Ruby-level location (file, line no in .rb) and we can get it by LEP(ep) basically (VM_ENV_FLAG_LOCAL flag is set) * But the control frame with VM_FRAME_MAGIC_CFUNC is also a LOCAL frame because it's a visible Ruby-level frame without block handlers * So, for the namespace detection, LEP(ep) is not enough and we need to skip CFUNC frames to fetch the caller of such frames --- namespace.c | 2 +- vm.c | 59 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/namespace.c b/namespace.c index e30d426d68b237..82bacd41081208 100644 --- a/namespace.c +++ b/namespace.c @@ -460,7 +460,7 @@ rb_namespace_s_current(VALUE recv) if (!rb_namespace_available()) return Qnil; - ns = rb_vm_caller_namespace(GET_EC()); + ns = rb_vm_current_namespace(GET_EC()); VM_ASSERT(ns && ns->ns_object); return ns->ns_object; } diff --git a/vm.c b/vm.c index 790a5af4f0cf7d..1a328fb63c6c3d 100644 --- a/vm.c +++ b/vm.c @@ -95,6 +95,29 @@ rb_vm_search_cf_from_ep(const rb_execution_context_t *ec, const rb_control_frame } } +static const VALUE * +VM_EP_RUBY_LEP(const rb_execution_context_t *ec, const rb_control_frame_t *current_cfp) +{ + const VALUE *ep = current_cfp->ep; + const rb_control_frame_t *cfp, *checkpoint_cfp = current_cfp; + + while (!VM_ENV_LOCAL_P(ep) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) { + while (!VM_ENV_LOCAL_P(ep)) { + ep = VM_ENV_PREV_EP(ep); + } + while (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) { + if (!cfp) { + cfp = rb_vm_search_cf_from_ep(ec, checkpoint_cfp, ep); + } + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + ep = cfp->ep; + } + checkpoint_cfp = cfp; + cfp = NULL; + } + return ep; +} + const VALUE * rb_vm_ep_local_ep(const VALUE *ep) { @@ -1001,11 +1024,8 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co VM_FORCE_WRITE_SPECIAL_CONST(&ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(prev_cfp->ep)); } } - else if (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)) { - // block_handler is always VM_BLOCK_HANDLER_NONE in this case - } else { - VM_ASSERT(VM_ENV_LOCAL_P(ep) && !VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)); + VM_ASSERT(VM_ENV_LOCAL_P(ep)); VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep); if (block_handler != VM_BLOCK_HANDLER_NONE) { @@ -3017,15 +3037,15 @@ rb_vm_frame_flag_set_ns_require(const rb_execution_context_t *ec) } static const rb_namespace_t * -current_namespace_on_env(const VALUE *ep) +current_namespace_on_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp) { rb_callable_method_entry_t *cme; const rb_namespace_t *ns; - const VALUE *lep = VM_EP_LEP(ep); + const VALUE *lep = VM_EP_RUBY_LEP(ec, cfp); VM_ASSERT(lep); VM_ASSERT(rb_namespace_available()); - if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_METHOD) || VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_CFUNC)) { + if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_METHOD)) { cme = check_method_entry(lep[VM_ENV_DATA_INDEX_ME_CREF], TRUE); VM_ASSERT(cme); VM_ASSERT(cme->def); @@ -3053,29 +3073,16 @@ const rb_namespace_t * rb_vm_current_namespace(const rb_execution_context_t *ec) { VM_ASSERT(rb_namespace_available()); - return current_namespace_on_env(ec->cfp->ep); -} - -const rb_namespace_t * -rb_vm_caller_namespace(const rb_execution_context_t *ec) -{ - const rb_control_frame_t *caller_cfp; - - VM_ASSERT(rb_namespace_available()); - - // The current control frame is MAGIC_CFUNC to call Namespace.current, but - // we want to get the current namespace of its caller. - caller_cfp = vm_get_ruby_level_caller_cfp(ec, ec->cfp); - return current_namespace_on_env(caller_cfp->ep); + return current_namespace_on_cfp(ec, ec->cfp); } static const rb_control_frame_t * -find_loader_control_frame(const rb_control_frame_t *cfp, const rb_control_frame_t *end_cfp) +find_loader_control_frame(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const rb_control_frame_t *end_cfp) { while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) { if (!VM_ENV_FRAME_TYPE_P(cfp->ep, VM_FRAME_MAGIC_CFUNC)) break; - if (!NAMESPACE_ROOT_P(current_namespace_on_env(cfp->ep))) + if (!NAMESPACE_ROOT_P(current_namespace_on_cfp(ec, cfp))) break; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); } @@ -3102,13 +3109,13 @@ rb_vm_loading_namespace(const rb_execution_context_t *ec) return rb_get_namespace_t(cfp->self); } // Kernel#require, #require_relative, #load - cfp = find_loader_control_frame(cfp, end_cfp); - return current_namespace_on_env(cfp->ep); + cfp = find_loader_control_frame(ec, cfp, end_cfp); + return current_namespace_on_cfp(ec, cfp); } cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); } // no require/load with explicit namespaces. - return current_namespace_on_env(current_cfp->ep); + return current_namespace_on_cfp(ec, current_cfp); } /* vm */ From 32f58628e900894b48b9e8630c250dedbbb1c126 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 10 Aug 2025 18:45:44 +0900 Subject: [PATCH 11/28] Update Namespace#eval to use control frames instead of namespace_push/pop With this change, the argument code of Namespace#eval cannot refer local variables around the calling line, but it should not be able to refer these values. The code is evaluated in the receiver namespace, independently from the local context. --- iseq.c | 15 +++++++++++++++ iseq.h | 1 + namespace.c | 18 +++++++++--------- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/iseq.c b/iseq.c index ad9149ef98e815..ae30d60ced9e3f 100644 --- a/iseq.c +++ b/iseq.c @@ -1161,6 +1161,21 @@ rb_iseq_load_iseq(VALUE fname) return NULL; } +const rb_iseq_t * +rb_iseq_compile_iseq(VALUE str, VALUE fname) +{ + VALUE args[] = { + str, fname + }; + VALUE iseqv = rb_check_funcall(rb_cISeq, rb_intern("compile"), 2, args); + + if (!SPECIAL_CONST_P(iseqv) && RBASIC_CLASS(iseqv) == rb_cISeq) { + return iseqw_check(iseqv); + } + + return NULL; +} + #define CHECK_ARRAY(v) rb_to_array_type(v) #define CHECK_HASH(v) rb_to_hash_type(v) #define CHECK_STRING(v) rb_str_to_str(v) diff --git a/iseq.h b/iseq.h index c7f091a0b4c2df..a8ad8ef9b064b8 100644 --- a/iseq.h +++ b/iseq.h @@ -192,6 +192,7 @@ void rb_iseq_init_trace(rb_iseq_t *iseq); int rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, bool target_bmethod); int rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval); const rb_iseq_t *rb_iseq_load_iseq(VALUE fname); +const rb_iseq_t *rb_iseq_compile_iseq(VALUE str, VALUE fname); int rb_iseq_opt_frozen_string_literal(void); #if VM_INSN_INFO_TABLE_IMPL == 2 diff --git a/namespace.c b/namespace.c index 82bacd41081208..3b7185e9aada1d 100644 --- a/namespace.c +++ b/namespace.c @@ -12,6 +12,7 @@ #include "internal/namespace.h" #include "internal/st.h" #include "internal/variable.h" +#include "iseq.h" #include "ruby/internal/globals.h" #include "ruby/util.h" #include "vm_core.h" @@ -842,21 +843,20 @@ initialize_root_namespace(void) } } -static VALUE -rb_namespace_eval_string(VALUE str) -{ - return rb_eval_string(RSTRING_PTR(str)); -} - static VALUE rb_namespace_eval(VALUE namespace, VALUE str) { - rb_thread_t *th = GET_THREAD(); + const rb_iseq_t *iseq; + const rb_namespace_t *ns; StringValue(str); - namespace_push(th, namespace); - return rb_ensure(rb_namespace_eval_string, str, namespace_pop, (VALUE)th); + iseq = rb_iseq_compile_iseq(str, rb_str_new_cstr("eval")); + VM_ASSERT(iseq); + + ns = (const rb_namespace_t *)rb_get_namespace_t(namespace); + + return rb_iseq_eval(iseq, ns); } static int namespace_experimental_warned = 0; From 53a1ff71e7dd898408b68c5c5c4429d0c93ba25f Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Mon, 11 Aug 2025 15:50:28 +0900 Subject: [PATCH 12/28] Add and fix dependencies --- depend | 26 +++++++++++++++++ yjit/src/cruby_bindings.inc.rs | 53 +--------------------------------- zjit/src/cruby_bindings.inc.rs | 3 +- 3 files changed, 28 insertions(+), 54 deletions(-) diff --git a/depend b/depend index fa17e2ea6a43f6..0f1d0d3af8c308 100644 --- a/depend +++ b/depend @@ -9263,6 +9263,26 @@ namespace.$(OBJEXT): $(top_srcdir)/internal/string.h namespace.$(OBJEXT): $(top_srcdir)/internal/variable.h namespace.$(OBJEXT): $(top_srcdir)/internal/vm.h namespace.$(OBJEXT): $(top_srcdir)/internal/warnings.h +namespace.$(OBJEXT): $(top_srcdir)/prism/defines.h +namespace.$(OBJEXT): $(top_srcdir)/prism/encoding.h +namespace.$(OBJEXT): $(top_srcdir)/prism/node.h +namespace.$(OBJEXT): $(top_srcdir)/prism/options.h +namespace.$(OBJEXT): $(top_srcdir)/prism/pack.h +namespace.$(OBJEXT): $(top_srcdir)/prism/parser.h +namespace.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h +namespace.$(OBJEXT): $(top_srcdir)/prism/prism.h +namespace.$(OBJEXT): $(top_srcdir)/prism/regexp.h +namespace.$(OBJEXT): $(top_srcdir)/prism/static_literals.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h +namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h namespace.$(OBJEXT): {$(VPATH)}assert.h namespace.$(OBJEXT): {$(VPATH)}atomic.h namespace.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -9279,6 +9299,7 @@ namespace.$(OBJEXT): {$(VPATH)}constant.h namespace.$(OBJEXT): {$(VPATH)}debug_counter.h namespace.$(OBJEXT): {$(VPATH)}defines.h namespace.$(OBJEXT): {$(VPATH)}encoding.h +namespace.$(OBJEXT): {$(VPATH)}eval_intern.h namespace.$(OBJEXT): {$(VPATH)}id.h namespace.$(OBJEXT): {$(VPATH)}id_table.h namespace.$(OBJEXT): {$(VPATH)}intern.h @@ -9433,12 +9454,17 @@ namespace.$(OBJEXT): {$(VPATH)}internal/value_type.h namespace.$(OBJEXT): {$(VPATH)}internal/variable.h namespace.$(OBJEXT): {$(VPATH)}internal/warning_push.h namespace.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +namespace.$(OBJEXT): {$(VPATH)}iseq.h namespace.$(OBJEXT): {$(VPATH)}method.h namespace.$(OBJEXT): {$(VPATH)}missing.h namespace.$(OBJEXT): {$(VPATH)}namespace.c namespace.$(OBJEXT): {$(VPATH)}node.h namespace.$(OBJEXT): {$(VPATH)}onigmo.h namespace.$(OBJEXT): {$(VPATH)}oniguruma.h +namespace.$(OBJEXT): {$(VPATH)}prism/ast.h +namespace.$(OBJEXT): {$(VPATH)}prism/diagnostic.h +namespace.$(OBJEXT): {$(VPATH)}prism/version.h +namespace.$(OBJEXT): {$(VPATH)}prism_compile.h namespace.$(OBJEXT): {$(VPATH)}ruby_assert.h namespace.$(OBJEXT): {$(VPATH)}ruby_atomic.h namespace.$(OBJEXT): {$(VPATH)}rubyparser.h diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index d9ef2d494f6d55..1e34440460edd1 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -259,33 +259,6 @@ pub const RSTRING_FSTR: ruby_rstring_flags = 536870912; pub type ruby_rstring_flags = u32; pub type st_data_t = ::std::os::raw::c_ulong; pub type st_index_t = st_data_t; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct st_hash_type { - pub compare: ::std::option::Option< - unsafe extern "C" fn(arg1: st_data_t, arg2: st_data_t) -> ::std::os::raw::c_int, - >, - pub hash: ::std::option::Option st_index_t>, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct st_table_entry { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct st_table { - pub entry_power: ::std::os::raw::c_uchar, - pub bin_power: ::std::os::raw::c_uchar, - pub size_ind: ::std::os::raw::c_uchar, - pub rebuilds_num: ::std::os::raw::c_uint, - pub type_: *const st_hash_type, - pub num_entries: st_index_t, - pub bins: *mut st_index_t, - pub entries_start: st_index_t, - pub entries_bound: st_index_t, - pub entries: *mut st_table_entry, -} pub const ST_CONTINUE: st_retval = 0; pub const ST_STOP: st_retval = 1; pub const ST_DELETE: st_retval = 2; @@ -373,28 +346,6 @@ pub const BOP_PACK: ruby_basic_operators = 32; pub const BOP_INCLUDE_P: ruby_basic_operators = 33; pub const BOP_LAST_: ruby_basic_operators = 34; pub type ruby_basic_operators = u32; -#[repr(C)] -pub struct rb_namespace_struct { - pub ns_object: VALUE, - pub ns_id: ::std::os::raw::c_long, - pub top_self: VALUE, - pub load_path: VALUE, - pub load_path_snapshot: VALUE, - pub load_path_check_cache: VALUE, - pub expanded_load_path: VALUE, - pub loaded_features: VALUE, - pub loaded_features_snapshot: VALUE, - pub loaded_features_realpaths: VALUE, - pub loaded_features_realpath_map: VALUE, - pub loaded_features_index: *mut st_table, - pub loading_table: *mut st_table, - pub ruby_dln_libmap: VALUE, - pub gvar_tbl: VALUE, - pub is_builtin: bool, - pub is_user: bool, - pub is_optional: bool, -} -pub type rb_namespace_t = rb_namespace_struct; pub type rb_serial_t = ::std::os::raw::c_ulonglong; pub const imemo_env: imemo_type = 0; pub const imemo_cref: imemo_type = 1; @@ -580,7 +531,6 @@ pub type rb_control_frame_t = rb_control_frame_struct; #[repr(C)] pub struct rb_proc_t { pub block: rb_block, - pub ns: *const rb_namespace_t, pub _bitfield_align_1: [u8; 0], pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>, pub __bindgen_padding_0: [u8; 7usize], @@ -676,8 +626,7 @@ pub const VM_FRAME_FLAG_LAMBDA: vm_frame_env_flags = 256; pub const VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM: vm_frame_env_flags = 512; pub const VM_FRAME_FLAG_CFRAME_KW: vm_frame_env_flags = 1024; pub const VM_FRAME_FLAG_PASSED: vm_frame_env_flags = 2048; -pub const VM_FRAME_FLAG_NS_SWITCH: vm_frame_env_flags = 4096; -pub const VM_FRAME_FLAG_LOAD_ISEQ: vm_frame_env_flags = 8192; +pub const VM_FRAME_FLAG_NS_REQUIRE: vm_frame_env_flags = 4096; pub const VM_ENV_FLAG_LOCAL: vm_frame_env_flags = 2; pub const VM_ENV_FLAG_ESCAPED: vm_frame_env_flags = 4; pub const VM_ENV_FLAG_WB_REQUIRED: vm_frame_env_flags = 8; diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 5a06d99f9ee661..17a2d5a63d6e6a 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -390,8 +390,7 @@ pub const VM_FRAME_FLAG_LAMBDA: vm_frame_env_flags = 256; pub const VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM: vm_frame_env_flags = 512; pub const VM_FRAME_FLAG_CFRAME_KW: vm_frame_env_flags = 1024; pub const VM_FRAME_FLAG_PASSED: vm_frame_env_flags = 2048; -pub const VM_FRAME_FLAG_NS_SWITCH: vm_frame_env_flags = 4096; -pub const VM_FRAME_FLAG_LOAD_ISEQ: vm_frame_env_flags = 8192; +pub const VM_FRAME_FLAG_NS_REQUIRE: vm_frame_env_flags = 4096; pub const VM_ENV_FLAG_LOCAL: vm_frame_env_flags = 2; pub const VM_ENV_FLAG_ESCAPED: vm_frame_env_flags = 4; pub const VM_ENV_FLAG_WB_REQUIRED: vm_frame_env_flags = 8; From f3f70323bb2d66b823f81c286463f91dbfe853fe Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Mon, 11 Aug 2025 16:14:30 +0900 Subject: [PATCH 13/28] Skip loading gem_prelude in wasm environment --- builtin.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/builtin.c b/builtin.c index 158b98568530cb..d91ab2157c0e71 100644 --- a/builtin.c +++ b/builtin.c @@ -77,5 +77,11 @@ Init_builtin(void) void Init_builtin_features(void) { + +#ifndef BUILTIN_BINARY_SIZE + load_with_builtin_functions("gem_prelude", NULL); + +#endif + } From 228d2c39f05fff9c056a02647a764e164cbd729f Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Mon, 11 Aug 2025 16:17:15 +0900 Subject: [PATCH 14/28] Stop using C23 spec: initialization with an empty struct --- namespace.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namespace.c b/namespace.c index 3b7185e9aada1d..c655a5e947f0ea 100644 --- a/namespace.c +++ b/namespace.c @@ -25,6 +25,10 @@ VALUE rb_mNamespaceLoader = 0; static rb_namespace_t root_namespace_data = { /* Initialize values lazily in Init_namespace() */ + (VALUE)NULL, 0, + (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, + (struct st_table *)NULL, (struct st_table *)NULL, (VALUE)NULL, (VALUE)NULL, + false, false }; static rb_namespace_t * root_namespace = &root_namespace_data; From bff625d2a673ea73c720cac50a9d596ab02432a8 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 10:08:20 +0900 Subject: [PATCH 15/28] add VM_ENV_NAMESPACED_P to unify/simplify/correct when SPECVAL has a namespace --- vm.c | 6 +++--- vm_core.h | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/vm.c b/vm.c index 1a328fb63c6c3d..6d63a5eeaff30f 100644 --- a/vm.c +++ b/vm.c @@ -142,10 +142,10 @@ static inline VALUE VM_CF_BLOCK_HANDLER(const rb_control_frame_t * const cfp) { const VALUE *ep; - if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_TOP) { + if (VM_ENV_NAMESPACED_P(cfp->ep)) { VM_ASSERT(VM_ENV_LOCAL_P(cfp->ep)); - /* Never set black_handler for VM_FRAME_MAGIC_TOP - * and the specval is used for namespace (rb_namespace_t) in the case + /* Never set black_handler for VM_FRAME_MAGIC_TOP or VM_FRAME_MAGIC_CLASS + * and the specval is used for namespace (rb_namespace_t) in these case */ return VM_BLOCK_HANDLER_NONE; } diff --git a/vm_core.h b/vm_core.h index fab440f1fd5175..51898f56f9c559 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1559,10 +1559,16 @@ VM_ENV_PREV_EP(const VALUE *ep) return VM_ENV_PREV_EP_UNCHECKED(ep); } +static inline bool +VM_ENV_NAMESPACED_P(const VALUE *ep) +{ + return VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CLASS) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP); +} + static inline VALUE VM_ENV_BLOCK_HANDLER(const VALUE *ep) { - if (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CLASS) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)) { + if (VM_ENV_NAMESPACED_P(ep)) { VM_ASSERT(VM_ENV_LOCAL_P(ep)); return VM_BLOCK_HANDLER_NONE; } @@ -1574,7 +1580,7 @@ VM_ENV_BLOCK_HANDLER(const VALUE *ep) static inline const rb_namespace_t * VM_ENV_NAMESPACE(const VALUE *ep) { - VM_ASSERT(VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CLASS) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_TOP)); + VM_ASSERT(VM_ENV_NAMESPACED_P(ep)); VM_ASSERT(VM_ENV_LOCAL_P(ep)); return (const rb_namespace_t *)GC_GUARDED_PTR_REF(ep[VM_ENV_DATA_INDEX_SPECVAL]); } From 2622d792969ab53275b84b6df6094902c6309c80 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 10:09:28 +0900 Subject: [PATCH 16/28] fix the wrong patch: 6cea12a4de44e0c072e33eca51b57965068b474a --- builtin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin.c b/builtin.c index d91ab2157c0e71..657143a739f649 100644 --- a/builtin.c +++ b/builtin.c @@ -78,7 +78,7 @@ void Init_builtin_features(void) { -#ifndef BUILTIN_BINARY_SIZE +#ifdef BUILTIN_BINARY_SIZE load_with_builtin_functions("gem_prelude", NULL); From 140bf4d8a035f669b24541ff71144d086588f7b5 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 11:34:30 +0900 Subject: [PATCH 17/28] localize rb_vm_t and minimize times of GET_VM() calls --- load.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/load.c b/load.c index 6f3da288aaafcc..cab3cdf3b20767 100644 --- a/load.c +++ b/load.c @@ -1149,21 +1149,23 @@ search_required(const rb_namespace_t *ns, VALUE fname, volatile VALUE *path, fea // Check if it's a statically linked extension when // not already a feature and not found as a dynamic library. - rb_vm_t *vm = GET_VM(); - 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. - // 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); - RB_GC_GUARD(lookup_name); - return 's'; + if (!ft && type != loadable_ext_rb) { + rb_vm_t *vm = GET_VM(); + if (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. + // 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); + RB_GC_GUARD(lookup_name); + return 's'; + } } } From f9ea85dd41f66f50447e55123e826726574cb418 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 11:36:04 +0900 Subject: [PATCH 18/28] delete unused code --- namespace.c | 134 ---------------------------------------------------- 1 file changed, 134 deletions(-) diff --git a/namespace.c b/namespace.c index c655a5e947f0ea..3ace63ffc4ab46 100644 --- a/namespace.c +++ b/namespace.c @@ -72,119 +72,6 @@ rb_main_namespace(void) return main_namespace; } -/* -static bool -namespace_ignore_builtin_primitive_methods_p(const rb_namespace_t *ns, rb_method_definition_t *def) -{ - if (!NAMESPACE_BUILTIN_P(ns)) { - return false; - } - / Primitive methods (just to call C methods) covers/hides the effective - namespaces, so ignore the methods' namespaces to expose user code's - namespace to the implementation. - / - if (def->type == VM_METHOD_TYPE_ISEQ) { - ID mid = def->original_id; - const char *path = RSTRING_PTR(pathobj_path(def->body.iseq.iseqptr->body->location.pathobj)); - if (strcmp(path, "") == 0) { - if (mid == rb_intern("class") || mid == rb_intern("clone") || - mid == rb_intern("tag") || mid == rb_intern("then") || - mid == rb_intern("yield_self") || mid == rb_intern("loop") || - mid == rb_intern("Float") || mid == rb_intern("Integer") - ) { - return true; - } - } - else if (strcmp(path, "") == 0) { - if (mid == rb_intern("warn")) { - return true; - } - } - else if (strcmp(path, "") == 0) { - if (mid == rb_intern("load")) - return true; - } - } - return false; -} - -static inline const rb_namespace_t * -block_proc_namespace(const VALUE procval) -{ - rb_proc_t *proc; - - if (procval) { - GetProcPtr(procval, proc); - return proc->ns; - } - else { - return NULL; - } -} - -static const rb_namespace_t * -current_namespace(bool permit_calling_builtin) -{ - / - * TODO: move this code to vm.c or somewhere else - * when it's fully updated with VM_FRAME_FLAG_* - / - const rb_callable_method_entry_t *cme; - const rb_namespace_t *ns; - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *cfp = ec->cfp; - rb_thread_t *th = rb_ec_thread_ptr(ec); - int calling = 1; - - if (!rb_namespace_available()) - return 0; - - if (th->namespaces && RARRAY_LEN(th->namespaces) > 0) { - // temp code to detect the context is in require/load - // TODO: this doesn't work well in optional namespaces - // calling = 0; - } - while (calling) { - const rb_namespace_t *proc_ns = NULL; - VALUE bh; - if (VM_FRAME_NS_SWITCH_P(cfp)) { - bh = rb_vm_frame_block_handler(cfp); - if (bh && vm_block_handler_type(bh) == block_handler_type_proc) { - proc_ns = block_proc_namespace(VM_BH_TO_PROC(bh)); - if (permit_calling_builtin || NAMESPACE_USER_P(proc_ns)) - return proc_ns; - } - } - cme = rb_vm_frame_method_entry(cfp); - if (cme && cme->def) { - ns = cme->def->ns; - if (ns) { - // this method is not a built-in class/module's method - // or a built-in primitive (Ruby) method - if (!namespace_ignore_builtin_primitive_methods_p(ns, cme->def)) { - if (permit_calling_builtin || (proc_ns && NAMESPACE_USER_P(proc_ns))) - return ns; - } - } - cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); - } - else { - calling = 0; - } - } - // not in namespace-marked method calls - ns = th->ns; - if (ns) { - return ns; - } - if (!main_namespace) { - // Namespaces are not ready to be created - return root_namespace; - } - return main_namespace; -} -*/ - const rb_namespace_t * rb_current_namespace(void) { @@ -204,27 +91,6 @@ rb_current_namespace(void) const rb_namespace_t * rb_loading_namespace(void) { - /* - VALUE namespace; - long len; - VALUE require_stack = GET_VM()->require_stack; - - if (!rb_namespace_available()) - return 0; - - if (!require_stack) { - return current_namespace(false); - } - if ((len = RARRAY_LEN(require_stack)) == 0) { - return current_namespace(false); - } - - if (!RB_TYPE_P(require_stack, T_ARRAY)) - rb_bug("require_stack is not an array: %s", rb_type_str(BUILTIN_TYPE(require_stack))); - - namespace = RARRAY_AREF(require_stack, len-1); - return rb_get_namespace_t(namespace); - */ if (!main_namespace) return root_namespace; From 58030884d80cb6f24f887c5914ceb572e996df4f Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 12 Aug 2025 15:04:31 -0700 Subject: [PATCH 19/28] YJIT: respect the code in master branch * Originally, k0kubun added a change to respect namespace in gen_block_given https://github.com/ruby/ruby/pull/13454/commits/d129669b1729b9570da7958394ea594031e79f59 * Just after the change, XrXr proposes a change on master and he says it can fix the problem around block_given?, and it has been merged into the master https://github.com/ruby/ruby/pull/14208 * tagomoris respect the commit on master then will see if it works --- yjit/src/codegen.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 67841d2fdc721e..25a8545e859172 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -6540,6 +6540,7 @@ fn jit_rb_f_block_given_p( true } +/// Codegen for `block_given?` and `defined?(yield)` fn gen_block_given( jit: &mut JITState, asm: &mut Assembler, From 81f3591b17bbcbc6081d9f80e2855fd109ec3830 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sat, 20 Sep 2025 14:15:42 +0900 Subject: [PATCH 20/28] Unify all block_handler getter to check namespace consistently --- vm_insnhelper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm_insnhelper.h b/vm_insnhelper.h index 8e7b7c743e7ebb..6d32a7535b3308 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -144,7 +144,7 @@ CC_SET_FASTPATH(const struct rb_callcache *cc, vm_call_handler func, bool enable } } -#define GET_BLOCK_HANDLER() VM_ENV_BLOCK_HANDLER(VM_EP_LEP(GET_EP())) +#define GET_BLOCK_HANDLER() VM_CF_BLOCK_HANDLER(GET_CFP()) /**********************************************************/ /* deal with control flow 3: exception */ From f58f7f25a38a452ac7b288dab2e463e2d3076db5 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sat, 20 Sep 2025 14:17:14 +0900 Subject: [PATCH 21/28] Fix bug of uninitialized variable, missed EoCFP, return values --- vm.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/vm.c b/vm.c index 6d63a5eeaff30f..1867f95fd9fc6a 100644 --- a/vm.c +++ b/vm.c @@ -99,18 +99,37 @@ static const VALUE * VM_EP_RUBY_LEP(const rb_execution_context_t *ec, const rb_control_frame_t *current_cfp) { const VALUE *ep = current_cfp->ep; - const rb_control_frame_t *cfp, *checkpoint_cfp = current_cfp; + const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */ + const rb_control_frame_t *cfp = NULL, *checkpoint_cfp = current_cfp; while (!VM_ENV_LOCAL_P(ep) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) { while (!VM_ENV_LOCAL_P(ep)) { ep = VM_ENV_PREV_EP(ep); } - while (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) { + while (VM_ENV_FLAGS(ep, VM_FRAME_FLAG_CFRAME) != 0) { if (!cfp) { cfp = rb_vm_search_cf_from_ep(ec, checkpoint_cfp, ep); + VM_ASSERT(cfp, "rb_vm_search_cf_from_ep should return a valid cfp for the ep"); + VM_ASSERT(cfp->ep == ep); + } + if (!cfp) { + return NULL; + } + VM_ASSERT(cfp->ep); + VM_ASSERT(cfp->ep == ep); + + if (VM_FRAME_FINISHED_P(cfp)) { + rb_bug("CFUNC frame should not FINISHED"); } cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + if (cfp >= eocfp) { + return NULL; + } + VM_ASSERT(cfp, "CFUNC should have a valid previous control frame"); ep = cfp->ep; + if (!ep) { + return NULL; + } } checkpoint_cfp = cfp; cfp = NULL; From 4644d14990df61b1fcc22cd9016a9f877ea29d32 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sat, 20 Sep 2025 14:44:35 +0900 Subject: [PATCH 22/28] Fix the missed vm_ns during rebase to follow the change b227a942b205c89fdb5adc85acdf029b9b83faf1 --- load.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/load.c b/load.c index cab3cdf3b20767..a0c45561d7eca4 100644 --- a/load.c +++ b/load.c @@ -390,10 +390,10 @@ get_loaded_features_index(const rb_namespace_t *ns) features_index_add(ns, as_str, INT2FIX(i)); } /* The user modified $LOADED_FEATURES, so we should restore the changes. */ - if (!rb_ary_shared_with_p(features, CURRENT_NS_LOADED_FEATURES(vm_ns))) { + if (!rb_ary_shared_with_p(features, ns->loaded_features)) { rb_ary_replace(ns->loaded_features, features); } - reset_loaded_features_snapshot(vm_ns); + reset_loaded_features_snapshot(ns); features = ns->loaded_features_snapshot; long j = RARRAY_LEN(features); From 9361af6ee7019958ea5d011d9e4428bf71f5e080 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sat, 20 Sep 2025 16:09:11 +0900 Subject: [PATCH 23/28] Update dependency after rebase --- depend | 3 +++ 1 file changed, 3 insertions(+) diff --git a/depend b/depend index 0f1d0d3af8c308..d5ac468b44209d 100644 --- a/depend +++ b/depend @@ -9263,7 +9263,9 @@ namespace.$(OBJEXT): $(top_srcdir)/internal/string.h namespace.$(OBJEXT): $(top_srcdir)/internal/variable.h namespace.$(OBJEXT): $(top_srcdir)/internal/vm.h namespace.$(OBJEXT): $(top_srcdir)/internal/warnings.h +namespace.$(OBJEXT): $(top_srcdir)/prism/ast.h namespace.$(OBJEXT): $(top_srcdir)/prism/defines.h +namespace.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h namespace.$(OBJEXT): $(top_srcdir)/prism/encoding.h namespace.$(OBJEXT): $(top_srcdir)/prism/node.h namespace.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -9283,6 +9285,7 @@ namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h namespace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +namespace.$(OBJEXT): $(top_srcdir)/prism/version.h namespace.$(OBJEXT): {$(VPATH)}assert.h namespace.$(OBJEXT): {$(VPATH)}atomic.h namespace.$(OBJEXT): {$(VPATH)}backward/2/assume.h From 88b5287d29b381126a4a5566005033acb269d874 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sat, 20 Sep 2025 16:33:27 +0900 Subject: [PATCH 24/28] re-implement free/memsize for rb_namespace_t correctly --- namespace.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/namespace.c b/namespace.c index 3ace63ffc4ab46..2aadd37208386b 100644 --- a/namespace.c +++ b/namespace.c @@ -186,27 +186,32 @@ rb_namespace_entry_mark(void *ptr) rb_gc_mark(ns->gvar_tbl); } -// TODO: implemente namespace_entry_free to free loading_table etc -/* static int free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg) { xfree((char *)key); return ST_DELETE; } - if (vm->loading_table) { - st_foreach(vm->loading_table, free_loading_table_entry, 0); - st_free_table(vm->loading_table); - vm->loading_table = 0; - } -*/ -#define namespace_entry_free RUBY_TYPED_DEFAULT_FREE + +static void +namespace_entry_free(void *ptr) +{ + rb_namespace_t *ns = (rb_namespace_t *)ptr; + if (ns->loading_table) { + st_foreach(ns->loading_table, free_loading_table_entry, 0); + st_free_table(ns->loading_table); + ns->loading_table = 0; + } + xfree(ptr); +} static size_t namespace_entry_memsize(const void *ptr) { - // TODO: rb_st_memsize(loaded_features_index) + rb_st_memsize(vm->loading_table) - return sizeof(rb_namespace_t); + const rb_namespace_t *ns = (const rb_namespace_t *)ptr; + return sizeof(rb_namespace_t) + \ + rb_st_memsize(ns->loaded_features_index) + \ + rb_st_memsize(ns->loading_table); } const rb_data_type_t rb_namespace_data_type = { From ccbf0662f826c38231e1c14b64695130e09dc582 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sat, 20 Sep 2025 16:46:30 +0900 Subject: [PATCH 25/28] No need to set namespace to the frame start evaluating main * rb_vm_current_namespace() returns main_namespace if it's ready, root_namespace otherwise on the top of main_stack. --- vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm.c b/vm.c index 1867f95fd9fc6a..524bde55e6447f 100644 --- a/vm.c +++ b/vm.c @@ -2972,7 +2972,7 @@ rb_iseq_eval_main(const rb_iseq_t *iseq) { rb_execution_context_t *ec = GET_EC(); VALUE val; - vm_set_main_stack(ec, iseq); // TODO: not need to set the namespace? + vm_set_main_stack(ec, iseq); val = vm_exec(ec); return val; } From 88d7ef4c2d3b0009cd38c1cc8d9382a237ae84a4 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 28 Sep 2025 17:39:06 +0900 Subject: [PATCH 26/28] calling free() here causes free for un-malloced memory --- namespace.c | 1 - 1 file changed, 1 deletion(-) diff --git a/namespace.c b/namespace.c index 2aadd37208386b..c61b879a88bd4b 100644 --- a/namespace.c +++ b/namespace.c @@ -202,7 +202,6 @@ namespace_entry_free(void *ptr) st_free_table(ns->loading_table); ns->loading_table = 0; } - xfree(ptr); } static size_t From 6e9a3412793a93e437aaed536f9e47a2f4ab9d80 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 28 Sep 2025 17:40:44 +0900 Subject: [PATCH 27/28] zeroing on the table to suppress unintentional call of classext_foreach --- class.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/class.c b/class.c index e133d1579b3dd3..68a56a129db72e 100644 --- a/class.c +++ b/class.c @@ -693,6 +693,10 @@ class_alloc0(enum ruby_value_type type, VALUE klass, bool namespaceable) RCLASS_SET_SUPER((VALUE)obj, 0); */ + if (namespaceable) { + ((struct RClass_namespaceable *)obj)->ns_classext_tbl = NULL; + } + RCLASS_PRIME_NS((VALUE)obj) = ns; // Classes/Modules defined in user namespaces are // writable directly because it exists only in a namespace. From 9d9390a33c2b9f4836bc7dfe6de1b497663f8b45 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 28 Sep 2025 21:32:57 +0900 Subject: [PATCH 28/28] Add methods for debugging only when RUBY_DEBUG --- namespace.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/namespace.c b/namespace.c index c61b879a88bd4b..f6f373094f1b5e 100644 --- a/namespace.c +++ b/namespace.c @@ -826,6 +826,18 @@ Init_enable_namespace(void) #ifdef RUBY_DEBUG +static VALUE +rb_namespace_s_root(VALUE recv) +{ + return root_namespace->ns_object; +} + +static VALUE +rb_namespace_s_main(VALUE recv) +{ + return main_namespace->ns_object; +} + static const char * classname(VALUE klass) { @@ -990,6 +1002,8 @@ Init_Namespace(void) rb_include_module(rb_cObject, rb_mNamespaceLoader); #ifdef RUBY_DEBUG + rb_define_singleton_method(rb_cNamespace, "root", rb_namespace_s_root, 0); + rb_define_singleton_method(rb_cNamespace, "main", rb_namespace_s_main, 0); rb_define_global_function("dump_classext", rb_f_dump_classext, 1); #endif }