From 4e127b09abc7e573af37dfa1615e6847d9ef7b57 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 15 Jun 2025 18:55:57 +0900 Subject: [PATCH 01/18] 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 | 441 +++++++++++++++---------------------------- 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, 489 insertions(+), 814 deletions(-) diff --git a/builtin.c b/builtin.c index 3400c4976e..158b985685 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 24f61fd023..26486074f8 100644 --- a/class.c +++ b/class.c @@ -648,7 +648,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 019a2d19a2..0ff59efd98 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 2c244aa5e0..6353319c6f 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 e0dab9e890..5209e429f9 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 f21a1810a5..512e136abb 100644 --- a/insns.def +++ b/insns.def @@ -802,12 +802,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 328d650e8b..20b99c7334 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 e618d87cc3..c1cf3db94d 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 4cdfbc305f..c68f0987aa 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 4334bdd795..682a2ad6cf 100644 --- a/iseq.c +++ b/iseq.c @@ -1955,7 +1955,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 017c236483..cb3b3143f4 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); + return get_expanded_load_path((rb_namespace_t *)rb_loading_namespace()); } static VALUE -load_path_getter(ID id, VALUE * p) +load_path_getter(ID _x, VALUE * _y) { - 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); -} - -static VALUE -get_loaded_features_realpath_map(vm_ns_t *vm_ns) -{ - 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) +get_loaded_features_index_raw(const rb_namespace_t *ns) { - return CURRENT_NS_LOADED_FEATURES_INDEX(vm_ns); -} - -static st_table * -get_loading_table(vm_ns_t *vm_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); @@ -454,11 +382,11 @@ 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)); } - reset_loaded_features_snapshot(vm_ns); + reset_loaded_features_snapshot(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); @@ -472,7 +400,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 @@ -557,7 +485,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; @@ -578,8 +506,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 @@ -626,7 +554,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; @@ -646,14 +574,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) { @@ -708,7 +636,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; @@ -720,15 +648,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; @@ -737,30 +665,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 @@ -770,8 +698,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)); @@ -789,35 +716,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 }; @@ -862,16 +771,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 @@ -889,7 +789,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()); @@ -1048,10 +948,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 */ @@ -1093,11 +993,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); } @@ -1176,10 +1076,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; @@ -1190,20 +1090,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'; } @@ -1212,25 +1112,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'; } @@ -1239,7 +1139,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. @@ -1249,7 +1150,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'; @@ -1261,7 +1162,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) { @@ -1270,7 +1171,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; } @@ -1291,9 +1192,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)); @@ -1313,7 +1214,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; } @@ -1325,11 +1226,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': @@ -1364,21 +1265,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) @@ -1398,14 +1284,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; @@ -1421,12 +1307,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) { @@ -1443,10 +1329,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); @@ -1454,19 +1340,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; @@ -1482,7 +1359,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) { @@ -1508,7 +1385,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); @@ -1611,15 +1488,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); } @@ -1764,7 +1642,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)) { @@ -1772,7 +1650,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; @@ -1781,7 +1659,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; } @@ -1791,31 +1669,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); @@ -1824,7 +1689,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 d9827e70e4..3ff3dc5c1d 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 55b7580c72..15ed1654c9 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; } @@ -787,108 +763,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 @@ -914,9 +861,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, @@ -929,13 +877,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 @@ -950,8 +897,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"); @@ -966,106 +913,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); -} - -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) -{ - rb_namespace_exec(ns, setup_pushing_loading_namespace_include, rb_mNamespaceLoader); + rb_vm_frame_flag_set_ns_require(GET_EC()); + return rb_call_super(argc, argv); } static void namespace_define_loader_method(const char *name) { - 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); + rb_define_private_method(rb_mNamespaceLoader, name, rb_namespace_loading_func, -1); + rb_define_singleton_method(rb_mNamespaceLoader, name, rb_namespace_loading_func, -1); +} + +void +Init_root_namespace(void) +{ + root_namespace->loading_table = st_init_strtable(); } void @@ -1100,17 +965,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); @@ -1123,7 +987,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 68f63040b7..11f09439a1 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 a01e3d8afa..929fabc568 100644 --- a/ruby.c +++ b/ruby.c @@ -440,7 +440,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; @@ -745,7 +745,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); @@ -2319,8 +2319,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 = @@ -2467,7 +2467,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; @@ -2490,11 +2490,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); @@ -2506,7 +2506,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 76b16b04cb..d6c08fe4ed 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; @@ -3283,43 +3284,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) { @@ -3333,26 +3297,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 4223c2d2ac..0551b99e51 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); } @@ -761,15 +769,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); @@ -975,7 +984,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) { @@ -1163,7 +1176,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)); @@ -1173,7 +1185,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; @@ -1205,12 +1216,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; @@ -2869,24 +2878,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_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); - // 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_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; } @@ -2896,8 +2892,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; } @@ -2937,10 +2932,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); @@ -2950,9 +2946,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; @@ -2960,7 +2958,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); @@ -2970,6 +2968,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 @@ -2980,22 +3058,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) { @@ -3067,29 +3136,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); @@ -3124,14 +3182,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 @@ -3153,7 +3203,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); @@ -3184,11 +3233,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; @@ -3288,8 +3332,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) + @@ -3552,8 +3594,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)); @@ -3680,6 +3720,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); @@ -3701,9 +3743,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; @@ -3733,16 +3778,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; } @@ -4289,8 +4328,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; @@ -4505,7 +4542,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(); } @@ -4541,17 +4577,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 569aebaba4..a8f1ca0a95 100644 --- a/vm_core.h +++ b/vm_core.h @@ -313,7 +313,6 @@ struct rb_calling_info { int argc; bool kw_splat; VALUE heap_argv; - const rb_namespace_t *proc_ns; }; #ifndef VM_ARGC_STACK_MAX @@ -751,20 +750,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; @@ -828,9 +817,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 */ @@ -1132,9 +1118,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 */ @@ -1274,7 +1257,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 */ @@ -1366,11 +1348,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) */ @@ -1393,10 +1375,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, @@ -1440,6 +1421,12 @@ VM_ENV_FLAGS(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) { @@ -1496,9 +1483,9 @@ VM_FRAME_RUBYFRAME_P(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) \ @@ -1523,10 +1510,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 @@ -1847,8 +1847,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 2a863ddef9..3f686bf7fa 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -1148,6 +1148,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) { @@ -1196,10 +1197,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 8ce7db1a80..6e54229b20 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -5161,20 +5161,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, @@ -5330,10 +5316,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, @@ -5434,9 +5416,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 24bfbb8210..2fdd08bc6e 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 bb0de4ba2c6b259cdfc3776d0aa0e3e01a5b981b Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 22 Jun 2025 17:38:24 +0900 Subject: [PATCH 02/18] 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 6353319c6f..4ac950e238 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 15ed1654c9..4af555b8b5 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 0551b99e51..bb3babf337 100644 --- a/vm.c +++ b/vm.c @@ -3011,13 +3011,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 5a491bcb2ba22e235fd561b1e77ca65b225e8cd0 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 22 Jun 2025 18:02:01 +0900 Subject: [PATCH 03/18] 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 4af555b8b5..9ad3089fe2 100644 --- a/namespace.c +++ b/namespace.c @@ -823,7 +823,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 d797d7b720e5298c535d08c7f48b5580040dd7d1 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 22 Jun 2025 18:03:57 +0900 Subject: [PATCH 04/18] 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 c68f0987aa..9ffc9a5c8b 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 9ad3089fe2..37de4103c3 100644 --- a/namespace.c +++ b/namespace.c @@ -873,7 +873,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 2c13376500ff4ace22bb8f2f1a23a9448d293610 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 24 Jun 2025 23:11:01 +0900 Subject: [PATCH 05/18] 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 bb3babf337..99bded89a5 100644 --- a/vm.c +++ b/vm.c @@ -777,7 +777,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 d7f757af1ed0345729af8b6e1c0f360fbf59632f Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 29 Jun 2025 09:42:22 +0900 Subject: [PATCH 06/18] 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 37de4103c3..87a7979e95 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; @@ -978,7 +978,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 476d925d466bb780e87c9c79ed3e45994adc3381 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 29 Jun 2025 09:44:31 +0900 Subject: [PATCH 07/18] 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 99bded89a5..ed5777a6f5 100644 --- a/vm.c +++ b/vm.c @@ -3024,10 +3024,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) { @@ -3041,14 +3055,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 d5876f1eca2a96eccf767d9e9d00cd630bc15ec0 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 27 Jul 2025 11:02:52 +0900 Subject: [PATCH 08/18] 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 87a7979e95..a3006c0f6f 100644 --- a/namespace.c +++ b/namespace.c @@ -946,6 +946,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 * @@ -973,8 +1107,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 67e8a81867e6903f24d3e58ee48424b81ca17bde Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 27 Jul 2025 11:04:15 +0900 Subject: [PATCH 09/18] 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 a3006c0f6f..c4bf62d252 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 ed5777a6f5..63edeadd9a 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) { @@ -984,11 +1007,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) { @@ -2976,15 +2996,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); @@ -3012,29 +3032,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); } @@ -3061,13 +3068,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 5b8d4ab516f0272dd4c7f34a4cc483b0577678db Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 10 Aug 2025 18:45:44 +0900 Subject: [PATCH 10/18] 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 682a2ad6cf..dad721f2ac 100644 --- a/iseq.c +++ b/iseq.c @@ -1143,6 +1143,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 1cecc6960d..b814a0c1e3 100644 --- a/iseq.h +++ b/iseq.h @@ -187,6 +187,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 c4bf62d252..51a936ca56 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" @@ -838,21 +839,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 d87f8abde4b47e229833e5624f3d72ad55e889bd Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Mon, 11 Aug 2025 15:50:28 +0900 Subject: [PATCH 11/18] 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 ea2486e9e8..95cb8afb22 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 c8a58f424e..bb11fc42f0 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; @@ -582,7 +533,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], @@ -678,8 +628,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 5c939fabe7..1a167a83a9 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -386,8 +386,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 6cea12a4de44e0c072e33eca51b57965068b474a Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Mon, 11 Aug 2025 16:14:30 +0900 Subject: [PATCH 12/18] Skip loading gem_prelude in wasm environment --- builtin.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/builtin.c b/builtin.c index 158b985685..d91ab2157c 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 b47a40b104b38b950f3674e8bf4e75bb4931b67c Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Mon, 11 Aug 2025 16:17:15 +0900 Subject: [PATCH 13/18] 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 51a936ca56..ff51ef7776 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 68503b80690297a0ce628df7a2ca16536e191d59 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 10:08:20 +0900 Subject: [PATCH 14/18] 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 63edeadd9a..e3a9c1846e 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 a8f1ca0a95..4e802cfe40 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1507,10 +1507,16 @@ VM_ENV_PREV_EP(const VALUE *ep) return GC_GUARDED_PTR_REF(ep[VM_ENV_DATA_INDEX_SPECVAL]); } +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; } @@ -1522,7 +1528,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 c1f3b1a3dc6dc307c2ec9a6e868dc9e0860b7a65 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 10:09:28 +0900 Subject: [PATCH 15/18] fix the wrong patch: 6cea12a4de44e0c072e33eca51b57965068b474a --- builtin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin.c b/builtin.c index d91ab2157c..657143a739 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 a8f6eaa6e229a21ac79225fdfe2a377c5f1944ba Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 11:34:30 +0900 Subject: [PATCH 16/18] 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 cb3b3143f4..79c62526a9 100644 --- a/load.c +++ b/load.c @@ -1139,21 +1139,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 b06465c4d3a2ab26c29b37586d98687d46bbe629 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 12 Aug 2025 11:36:04 +0900 Subject: [PATCH 17/18] delete unused code --- namespace.c | 134 ---------------------------------------------------- 1 file changed, 134 deletions(-) diff --git a/namespace.c b/namespace.c index ff51ef7776..2ccc3db94d 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 d129669b1729b9570da7958394ea594031e79f59 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 12 Aug 2025 15:04:31 -0700 Subject: [PATCH 18/18] YJIT: Check VM_ENV_NAMESPACED_P in gen_block_given --- yjit/src/codegen.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index b8b15adc8b..7b52774843 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -6647,6 +6647,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, @@ -6656,15 +6657,24 @@ fn gen_block_given( ) { asm_comment!(asm, "block_given?"); - // Same as rb_vm_frame_block_handler + // VM_ENV_FLAGS(ep, VM_FRAME_MAGIC_MASK) let ep_opnd = gen_get_lep(jit, asm); - let block_handler = asm.load( - Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL) - ); + let flags = Opnd::mem(64, ep_opnd, VM_ENV_DATA_INDEX_FLAGS as i32 * SIZEOF_VALUE_I32); + let frame_type = asm.and(flags, VM_FRAME_MAGIC_MASK.into()); + + // Return false_opnd if VM_ENV_NAMESPACED_P(ep) + asm.cmp(frame_type, VM_FRAME_MAGIC_CLASS.into()); + let block_given = asm.csel_ne(true_opnd, false_opnd); + asm.cmp(frame_type, VM_FRAME_MAGIC_TOP.into()); + let block_given = asm.csel_ne(block_given, false_opnd); + + // VM_ENV_BLOCK_HANDLER(VM_CF_LEP(cfp)) + let ep_opnd = gen_get_lep(jit, asm); // Reload ep_opnd to avoid register spill + let block_handler = Opnd::mem(64, ep_opnd, VM_ENV_DATA_INDEX_SPECVAL * SIZEOF_VALUE_I32); // Return `block_handler != VM_BLOCK_HANDLER_NONE` asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into()); - let block_given = asm.csel_ne(true_opnd, false_opnd); + let block_given = asm.csel_ne(block_given, false_opnd); asm.mov(out_opnd, block_given); }