summaryrefslogtreecommitdiff
path: root/namespace.c
diff options
context:
space:
mode:
Diffstat (limited to 'namespace.c')
-rw-r--r--namespace.c1129
1 files changed, 0 insertions, 1129 deletions
diff --git a/namespace.c b/namespace.c
deleted file mode 100644
index 55b7580c72..0000000000
--- a/namespace.c
+++ /dev/null
@@ -1,1129 +0,0 @@
-/* indent-tabs-mode: nil */
-
-#include "internal.h"
-#include "internal/class.h"
-#include "internal/eval.h"
-#include "internal/error.h"
-#include "internal/file.h"
-#include "internal/gc.h"
-#include "internal/hash.h"
-#include "internal/load.h"
-#include "internal/namespace.h"
-#include "internal/st.h"
-#include "internal/variable.h"
-#include "ruby/internal/globals.h"
-#include "ruby/util.h"
-#include "vm_core.h"
-
-#include <stdio.h>
-
-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 * const root_namespace = 0;
-static rb_namespace_t * const builtin_namespace = &builtin_namespace_data;
-static rb_namespace_t * main_namespace = 0;
-static char *tmp_dir;
-static bool tmp_dir_has_dirsep;
-
-#define NAMESPACE_TMP_PREFIX "_ruby_ns_"
-
-#ifndef MAXPATHLEN
-# define MAXPATHLEN 1024
-#endif
-
-#if defined(_WIN32)
-# define DIRSEP "\\"
-#else
-# define DIRSEP "/"
-#endif
-
-bool ruby_namespace_enabled = false; // extern
-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)
-{
- 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 *
-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
- 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, "<internal:kernel>") == 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, "<internal:warning>") == 0) {
- if (mid == rb_intern("warn")) {
- return true;
- }
- }
- else if (strcmp(path, "<internal:marshal>") == 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)
-{
- return current_namespace(true);
-}
-
-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);
-}
-
-const rb_namespace_t *
-rb_definition_namespace(void)
-{
- const rb_namespace_t *ns = current_namespace(true);
- if (NAMESPACE_BUILTIN_P(ns)) {
- return root_namespace;
- }
- return ns;
-}
-
-static long namespace_id_counter = 0;
-
-static long
-namespace_generate_id(void)
-{
- long id;
- RB_VM_LOCKING() {
- id = ++namespace_id_counter;
- }
- return id;
-}
-
-static void
-namespace_entry_initialize(rb_namespace_t *ns)
-{
- rb_vm_t *vm = GET_VM();
-
- // 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->load_path_snapshot = rb_ary_new();
- ns->load_path_check_cache = 0;
- ns->loaded_features = rb_ary_dup(vm->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->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)
-{
- rb_namespace_t *ns = (rb_namespace_t *)ptr;
- if (!NIL_P(ns->ns_object))
- ns->ns_object = rb_gc_location(ns->ns_object);
- 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);
- if (ns->load_path_check_cache) {
- ns->load_path_check_cache = rb_gc_location(ns->load_path_check_cache);
- }
- ns->loaded_features = rb_gc_location(ns->loaded_features);
- ns->loaded_features_snapshot = rb_gc_location(ns->loaded_features_snapshot);
- ns->loaded_features_realpaths = rb_gc_location(ns->loaded_features_realpaths);
- ns->loaded_features_realpath_map = rb_gc_location(ns->loaded_features_realpath_map);
- ns->ruby_dln_libmap = rb_gc_location(ns->ruby_dln_libmap);
- ns->gvar_tbl = rb_gc_location(ns->gvar_tbl);
-}
-
-void
-rb_namespace_entry_mark(void *ptr)
-{
- const rb_namespace_t *ns = (rb_namespace_t *)ptr;
- rb_gc_mark(ns->ns_object);
- rb_gc_mark(ns->top_self);
- rb_gc_mark(ns->load_path);
- rb_gc_mark(ns->expanded_load_path);
- rb_gc_mark(ns->load_path_snapshot);
- rb_gc_mark(ns->load_path_check_cache);
- rb_gc_mark(ns->loaded_features);
- rb_gc_mark(ns->loaded_features_snapshot);
- rb_gc_mark(ns->loaded_features_realpaths);
- rb_gc_mark(ns->loaded_features_realpath_map);
- if (ns->loading_table) {
- rb_mark_tbl(ns->loading_table);
- }
- rb_gc_mark(ns->ruby_dln_libmap);
- rb_gc_mark(ns->gvar_tbl);
-}
-
-#define namespace_entry_free RUBY_TYPED_DEFAULT_FREE
-
-static size_t
-namespace_entry_memsize(const void *ptr)
-{
- return sizeof(rb_namespace_t);
-}
-
-const rb_data_type_t rb_namespace_data_type = {
- "Namespace::Entry",
- {
- rb_namespace_entry_mark,
- namespace_entry_free,
- namespace_entry_memsize,
- rb_namespace_gc_update_references,
- },
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY // TODO: enable RUBY_TYPED_WB_PROTECTED when inserting write barriers
-};
-
-VALUE
-rb_namespace_entry_alloc(VALUE klass)
-{
- rb_namespace_t *entry;
- VALUE obj = TypedData_Make_Struct(klass, rb_namespace_t, &rb_namespace_data_type, entry);
- namespace_entry_initialize(entry);
- return obj;
-}
-
-static rb_namespace_t *
-get_namespace_struct_internal(VALUE entry)
-{
- rb_namespace_t *sval;
- TypedData_Get_Struct(entry, rb_namespace_t, &rb_namespace_data_type, sval);
- return sval;
-}
-
-rb_namespace_t *
-rb_get_namespace_t(VALUE namespace)
-{
- VALUE entry;
- ID id_namespace_entry;
-
- if (!namespace)
- return root_namespace;
- if (NIL_P(namespace))
- return builtin_namespace;
-
- CONST_ID(id_namespace_entry, "__namespace_entry__");
- entry = rb_attr_get(namespace, id_namespace_entry);
- return get_namespace_struct_internal(entry);
-}
-
-VALUE
-rb_get_namespace_object(rb_namespace_t *ns)
-{
- if (!ns) // root namespace
- return Qfalse;
- return ns->ns_object;
-}
-
-static void setup_pushing_loading_namespace(rb_namespace_t *ns);
-
-/*
- * call-seq:
- * Namespace.new -> new_namespace
- *
- * Returns a new Namespace object.
- */
-static VALUE
-namespace_initialize(VALUE namespace)
-{
- rb_namespace_t *ns;
- rb_classext_t *object_classext;
- VALUE entry;
- ID id_namespace_entry;
- CONST_ID(id_namespace_entry, "__namespace_entry__");
-
- if (!rb_namespace_available()) {
- rb_raise(rb_eRuntimeError, "Namespace is disabled. Set RUBY_NAMESPACE=1 environment variable to use Namespace.");
- }
-
- entry = rb_class_new_instance_pass_kw(0, NULL, rb_cNamespaceEntry);
- ns = get_namespace_struct_internal(entry);
-
- 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
- // constant table from any view of every (including main) namespace.
- // If a code in the namespace adds a constant, the constant will be visible even from root/main.
- RCLASS_SET_PRIME_CLASSEXT_WRITABLE(namespace, true);
-
- // Get a clean constant table of Object even by writable one
- // because ns was just created, so it has not touched any constants yet.
- object_classext = RCLASS_EXT_WRITABLE_IN_NS(rb_cObject, ns);
- RCLASS_SET_CONST_TBL(namespace, RCLASSEXT_CONST_TBL(object_classext), true);
-
- rb_ivar_set(namespace, id_namespace_entry, entry);
-
- setup_pushing_loading_namespace(ns);
-
- return namespace;
-}
-
-/*
- * call-seq:
- * Namespace.enabled? -> true or false
- *
- * Returns +true+ if namespace is enabled.
- */
-static VALUE
-rb_namespace_s_getenabled(VALUE namespace)
-{
- return RBOOL(rb_namespace_available());
-}
-
-/*
- * call-seq:
- * Namespace.current -> namespace, nil or false
- *
- * Returns the current namespace.
- * Returns +nil+ if it is the built-in namespace.
- * Returns +false+ if namespace is not enabled.
- */
-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)) {
- return Qnil;
- }
- return Qfalse;
-}
-
-/*
- * call-seq:
- * Namespace.is_builtin?(klass) -> true or false
- *
- * Returns +true+ if +klass+ is only in a user namespace.
- */
-static VALUE
-rb_namespace_s_is_builtin_p(VALUE namespace, VALUE klass)
-{
- if (RCLASS_PRIME_CLASSEXT_READABLE_P(klass) && !RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass))
- return Qtrue;
- return Qfalse;
-}
-
-/*
- * call-seq:
- * load_path -> array
- *
- * Returns namespace local load path.
- */
-static VALUE
-rb_namespace_load_path(VALUE namespace)
-{
- return rb_get_namespace_t(namespace)->load_path;
-}
-
-#ifdef _WIN32
-UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
-#endif
-
-/* Copied from mjit.c Ruby 3.0.3 */
-static char *
-system_default_tmpdir(void)
-{
- // c.f. ext/etc/etc.c:etc_systmpdir()
-#ifdef _WIN32
- WCHAR tmppath[_MAX_PATH];
- UINT len = rb_w32_system_tmpdir(tmppath, numberof(tmppath));
- if (len) {
- int blen = WideCharToMultiByte(CP_UTF8, 0, tmppath, len, NULL, 0, NULL, NULL);
- char *tmpdir = xmalloc(blen + 1);
- WideCharToMultiByte(CP_UTF8, 0, tmppath, len, tmpdir, blen, NULL, NULL);
- tmpdir[blen] = '\0';
- return tmpdir;
- }
-#elif defined _CS_DARWIN_USER_TEMP_DIR
- char path[MAXPATHLEN];
- size_t len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
- if (len > 0) {
- char *tmpdir = xmalloc(len);
- if (len > sizeof(path)) {
- confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdir, len);
- }
- else {
- memcpy(tmpdir, path, len);
- }
- return tmpdir;
- }
-#endif
- return 0;
-}
-
-static int
-check_tmpdir(const char *dir)
-{
- struct stat st;
-
- if (!dir) return FALSE;
- if (stat(dir, &st)) return FALSE;
-#ifndef S_ISDIR
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
-#endif
- if (!S_ISDIR(st.st_mode)) return FALSE;
-#ifndef _WIN32
-# ifndef S_IWOTH
-# define S_IWOTH 002
-# endif
- if (st.st_mode & S_IWOTH) {
-# ifdef S_ISVTX
- if (!(st.st_mode & S_ISVTX)) return FALSE;
-# else
- return FALSE;
-# endif
- }
- if (access(dir, W_OK)) return FALSE;
-#endif
- return TRUE;
-}
-
-static char *
-system_tmpdir(void)
-{
- char *tmpdir;
-# define RETURN_ENV(name) \
- if (check_tmpdir(tmpdir = getenv(name))) return ruby_strdup(tmpdir)
- RETURN_ENV("TMPDIR");
- RETURN_ENV("TMP");
- tmpdir = system_default_tmpdir();
- if (check_tmpdir(tmpdir)) return tmpdir;
- return ruby_strdup("/tmp");
-# undef RETURN_ENV
-}
-
-/* end of copy */
-
-static int
-sprint_ext_filename(char *str, size_t size, long namespace_id, const char *prefix, const char *basename)
-{
- if (tmp_dir_has_dirsep) {
- return snprintf(str, size, "%s%sp%"PRI_PIDT_PREFIX"u_%ld_%s", tmp_dir, prefix, getpid(), namespace_id, basename);
- }
- return snprintf(str, size, "%s%s%sp%"PRI_PIDT_PREFIX"u_%ld_%s", tmp_dir, DIRSEP, prefix, getpid(), namespace_id, basename);
-}
-
-#ifdef _WIN32
-static const char *
-copy_ext_file_error(char *message, size_t size)
-{
- int error = GetLastError();
- char *p = message;
- size_t len = snprintf(message, size, "%d: ", error);
-
-#define format_message(sublang) FormatMessage(\
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \
- NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \
- message + len, size - len, NULL)
- if (format_message(SUBLANG_ENGLISH_US) == 0)
- format_message(SUBLANG_DEFAULT);
- for (p = message + len; *p; p++) {
- if (*p == '\n' || *p == '\r')
- *p = ' ';
- }
- return message;
-}
-#else
-static const char *
-copy_ext_file_error(char *message, size_t size, int copy_retvalue, char *src_path, char *dst_path)
-{
- switch (copy_retvalue) {
- case 1:
- snprintf(message, size, "can't open the extension path: %s", src_path);
- case 2:
- snprintf(message, size, "can't open the file to write: %s", dst_path);
- case 3:
- snprintf(message, size, "failed to read the extension path: %s", src_path);
- case 4:
- snprintf(message, size, "failed to write the extension path: %s", dst_path);
- default:
- rb_bug("unknown return value of copy_ext_file: %d", copy_retvalue);
- }
- return message;
-}
-#endif
-
-static int
-copy_ext_file(char *src_path, char *dst_path)
-{
-#if defined(_WIN32)
- int rvalue;
-
- WCHAR *w_src = rb_w32_mbstr_to_wstr(CP_UTF8, src_path, -1, NULL);
- WCHAR *w_dst = rb_w32_mbstr_to_wstr(CP_UTF8, dst_path, -1, NULL);
- if (!w_src || !w_dst) {
- rb_memerror();
- }
-
- rvalue = CopyFileW(w_src, w_dst, FALSE) ? 0 : 1;
- free(w_src);
- free(w_dst);
- return rvalue;
-#else
- FILE *src, *dst;
- char buffer[1024];
- size_t read = 0, wrote, written = 0;
- size_t maxread = sizeof(buffer);
- int eof = 0;
- int clean_read = 1;
- int retvalue = 0;
-
- src = fopen(src_path, "rb");
- if (!src) {
- return 1;
- }
- dst = fopen(dst_path, "wb");
- if (!dst) {
- return 2;
- }
- while (!eof) {
- if (clean_read) {
- read = fread(buffer, 1, sizeof(buffer), src);
- written = 0;
- }
- if (read > 0) {
- wrote = fwrite(buffer+written, 1, read-written, dst);
- if (wrote < read-written) {
- if (ferror(dst)) {
- retvalue = 4;
- break;
- }
- else { // partial write
- clean_read = 0;
- written += wrote;
- }
- }
- else { // Wrote the entire buffer to dst, next read is clean one
- clean_read = 1;
- }
- }
- if (read < maxread) {
- if (clean_read && feof(src)) {
- // If it's not clean, buffer should have bytes not written yet.
- eof = 1;
- }
- else if (ferror(src)) {
- retvalue = 3;
- // Writes could be partial/dirty, but this load is failure anyway
- break;
- }
- }
- }
- fclose(src);
- fclose(dst);
- return retvalue;
-#endif
-}
-
-#if defined __CYGWIN__ || defined DOSISH
-#define isdirsep(x) ((x) == '/' || (x) == '\\')
-#else
-#define isdirsep(x) ((x) == '/')
-#endif
-
-#define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0)
-#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
-
-static void
-fname_without_suffix(char *fname, char *rvalue)
-{
- char *pos;
- strcpy(rvalue, fname);
- for (pos = rvalue + strlen(fname); pos > rvalue; pos--) {
- if (IS_SOEXT(pos) || IS_DLEXT(pos)) {
- *pos = '\0';
- return;
- }
- }
-}
-
-static void
-escaped_basename(char *path, char *fname, char *rvalue)
-{
- char *pos, *leaf, *found;
- leaf = path;
- // `leaf + 1` looks uncomfortable (when leaf == path), but fname must not be the top-dir itself
- while ((found = strstr(leaf + 1, fname)) != NULL) {
- leaf = found; // find the last occurrence for the path like /etc/my-crazy-lib-dir/etc.so
- }
- strcpy(rvalue, leaf);
- for (pos = rvalue; *pos; pos++) {
- if (isdirsep(*pos)) {
- *pos = '+';
- }
- }
-}
-
-VALUE
-rb_namespace_local_extension(VALUE namespace, VALUE fname, VALUE path)
-{
- char ext_path[MAXPATHLEN], fname2[MAXPATHLEN], basename[MAXPATHLEN];
- int copy_error, wrote;
- char *src_path = RSTRING_PTR(path), *fname_ptr = RSTRING_PTR(fname);
- rb_namespace_t *ns = rb_get_namespace_t(namespace);
-
- fname_without_suffix(fname_ptr, fname2);
- escaped_basename(src_path, fname2, basename);
-
- wrote = sprint_ext_filename(ext_path, sizeof(ext_path), ns->ns_id, NAMESPACE_TMP_PREFIX, basename);
- if (wrote >= (int)sizeof(ext_path)) {
- rb_bug("Extension file path in namespace was too long");
- }
- copy_error = copy_ext_file(src_path, ext_path);
- if (copy_error) {
- char message[1024];
-#if defined(_WIN32)
- copy_ext_file_error(message, sizeof(message));
-#else
- copy_ext_file_error(message, sizeof(message), copy_error, src_path, ext_path);
-#endif
- rb_raise(rb_eLoadError, "can't prepare the extension file for namespaces (%s from %s): %s", ext_path, src_path, message);
- }
- // TODO: register the path to be clean-uped
- return rb_str_new_cstr(ext_path);
-}
-
-// TODO: delete it just after dln_load? or delay it?
-// 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);
-
- 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);
-}
-
-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);
-}
-
-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);
-}
-
-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();
-
- StringValue(str);
-
- namespace_push(th, namespace);
- return rb_ensure(rb_namespace_eval_string, str, namespace_pop, (VALUE)th);
-}
-
-static int namespace_experimental_warned = 0;
-
-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;
-
- if (!namespace_experimental_warned) {
- rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL,
- "Namespace is experimental, and the behavior may change in the future!\n"
- "See doc/namespace.md for known issues, etc.");
- namespace_experimental_warned = 1;
- }
-
- main_ns = rb_class_new_instance_pass_kw(0, NULL, rb_cNamespace);
- 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;
-}
-
-static VALUE
-rb_namespace_inspect(VALUE obj)
-{
- rb_namespace_t *ns;
- VALUE r;
- if (obj == Qfalse) {
- r = rb_str_new_cstr("#<Namespace:root>");
- return r;
- }
- ns = rb_get_namespace_t(obj);
- r = rb_str_new_cstr("#<Namespace:");
- rb_str_concat(r, rb_funcall(LONG2NUM(ns->ns_id), rb_intern("to_s"), 0));
- if (NAMESPACE_BUILTIN_P(ns)) {
- rb_str_cat_cstr(r, ",builtin");
- }
- if (NAMESPACE_USER_P(ns)) {
- rb_str_cat_cstr(r, ",user");
- }
- if (NAMESPACE_MAIN_P(ns)) {
- rb_str_cat_cstr(r, ",main");
- }
- else if (NAMESPACE_OPTIONAL_P(ns)) {
- rb_str_cat_cstr(r, ",optional");
- }
- rb_str_cat_cstr(r, ">");
- return r;
-}
-
-struct refiner_calling_super_data {
- int argc;
- VALUE *argv;
-};
-
-static VALUE
-namespace_builtin_refiner_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_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);
-}
-
-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);
-}
-
-void
-Init_enable_namespace(void)
-{
- const char *env = getenv("RUBY_NAMESPACE");
- if (env && strlen(env) == 1 && env[0] == '1') {
- ruby_namespace_enabled = true;
- }
- else {
- ruby_namespace_init_done = true;
- }
-}
-
-/*
- * Document-class: Namespace
- *
- * Namespace is designed to provide separated spaces in a Ruby
- * process, to isolate applications and libraries.
- * See {Namespace}[rdoc-ref:namespace.md].
- */
-void
-Init_Namespace(void)
-{
- tmp_dir = system_tmpdir();
- tmp_dir_has_dirsep = (strcmp(tmp_dir + (strlen(tmp_dir) - strlen(DIRSEP)), DIRSEP) == 0);
-
- rb_cNamespace = rb_define_class("Namespace", rb_cModule);
- rb_define_method(rb_cNamespace, "initialize", namespace_initialize, 0);
-
- /* :nodoc: */
- 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);
- }
-
- /* :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");
-
- 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, "is_builtin?", rb_namespace_s_is_builtin_p, 1);
-
- rb_define_method(rb_cNamespace, "load_path", rb_namespace_load_path, 0);
- rb_define_method(rb_cNamespace, "load", rb_namespace_load, -1);
- rb_define_method(rb_cNamespace, "require", rb_namespace_require, 1);
- rb_define_method(rb_cNamespace, "require_relative", rb_namespace_require_relative, 1);
- 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();
-}