From f3e7330d9287152eda4b164d6cf2382115140446 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 16 Nov 2025 20:10:23 -0500 Subject: [PATCH] xo-interpreter adds + explict mm arg to ctors (retiring Object::mm) --- CMakeLists.txt | 3 +- docs/install.rst | 16 +++ xo-alloc/include/xo/alloc/GC.hpp | 2 + xo-alloc/include/xo/alloc/Object.hpp | 3 +- xo-alloc/src/alloc/ArenaAlloc.cpp | 71 +++++++--- xo-alloc/src/alloc/CMakeLists.txt | 4 +- xo-alloc/src/alloc/GC.cpp | 74 ++++++---- xo-alloc/src/alloc/Object.cpp | 2 +- xo-alloc/utest/Forwarding1.test.cpp | 2 +- xo-imgui/example/ex2/imgui_ex2.cpp | 14 +- xo-interpreter/CMakeLists.txt | 3 +- .../cmake/xo-bootstrap-macros.cmake | 35 +++++ .../cmake/xo_interpreterConfig.cmake.in | 7 + xo-interpreter/docs/CMakeLists.txt | 9 ++ xo-interpreter/docs/conf.py | 39 +++++ .../include/xo/interpreter/StackFrame.hpp | 52 ++++++- .../xo/interpreter/init_interpreter.hpp | 21 +++ xo-interpreter/src/interpreter/CMakeLists.txt | 13 ++ xo-interpreter/src/interpreter/StackFrame.cpp | 120 ++++++++++++++++ .../src/interpreter/init_interpreter.cpp | 27 ++++ xo-interpreter/utest/CMakeLists.txt | 12 ++ xo-interpreter/utest/StackFrame.test.cpp | 133 ++++++++++++++++++ .../utest/interpreter_utest_main.cpp | 6 + xo-jit/utest/CMakeLists.txt | 2 +- xo-object/include/xo/object/Integer.hpp | 3 +- xo-object/src/object/CMakeLists.txt | 4 + xo-object/src/object/Integer.cpp | 5 +- xo-object/src/object/List.cpp | 2 +- xo-object/src/object/String.cpp | 2 +- xo-object/utest/GC.test.cpp | 24 ++-- xo-object/utest/Integer.test.cpp | 4 +- xo-object/utest/List.test.cpp | 5 +- xo-ratio/utest/CMakeLists.txt | 2 +- xo-reader/docs/install.rst | 7 +- .../include/xo/reflect/EstablishTypeDescr.hpp | 8 +- xo-reflect/src/reflect/CMakeLists.txt | 2 +- xo-reflect/src/reflect/TypeDescr.cpp | 21 ++- xo-reflect/src/reflect/init_reflect.cpp | 20 +-- 38 files changed, 670 insertions(+), 109 deletions(-) create mode 100644 xo-interpreter/cmake/xo-bootstrap-macros.cmake create mode 100644 xo-interpreter/cmake/xo_interpreterConfig.cmake.in create mode 100644 xo-interpreter/docs/CMakeLists.txt create mode 100644 xo-interpreter/docs/conf.py create mode 100644 xo-interpreter/include/xo/interpreter/init_interpreter.hpp create mode 100644 xo-interpreter/src/interpreter/CMakeLists.txt create mode 100644 xo-interpreter/src/interpreter/StackFrame.cpp create mode 100644 xo-interpreter/src/interpreter/init_interpreter.cpp create mode 100644 xo-interpreter/utest/CMakeLists.txt create mode 100644 xo-interpreter/utest/StackFrame.test.cpp create mode 100644 xo-interpreter/utest/interpreter_utest_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 30344e54..68b484b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,7 @@ add_subdirectory(xo-expression) add_subdirectory(xo-pyexpression) add_subdirectory(xo-tokenizer) add_subdirectory(xo-reader) +add_subdirectory(xo-interpreter) add_subdirectory(xo-jit) add_subdirectory(xo-pyjit) # @@ -124,6 +125,6 @@ add_subdirectory(xo-imgui) # ---------------------------------------------------------------- # documentation. must follow add_subdirectory() for satellite projects -xo_umbrella_doxygen_deps(xo_alloc indentlog xo_flatstring xo_ratio xo_unit xo_tokenizer xo_reader xo_jit) +xo_umbrella_doxygen_deps(xo_alloc indentlog xo_flatstring xo_ratio xo_unit xo_tokenizer xo_reader xo_interpreter xo_jit) xo_umbrella_doxygen_config() xo_umbrella_sphinx_config(index.rst docs/install.rst docs/glossary.rst) diff --git a/docs/install.rst b/docs/install.rst index 2e77000f..f2fea03b 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -177,15 +177,31 @@ To setup a unit test coverage build/ccov/all-merged Then run unit tests +.. code-block:: + $ (cd .build && ctest) To build coverage report +.. code-block:: + $ (.build/gen-ccov) Html report in ``.build/ccov/html/index.html`` +Address Sanitizer Setup +----------------------- + +To run address sanitizer + +.. code-block:: + + # can reuse phase 1 cmake-macros-install + + # phase 2 + $ cmake -B .build -S . -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_BUILD_TYPE=asan + Sphinx Autobuild Setup ---------------------- diff --git a/xo-alloc/include/xo/alloc/GC.hpp b/xo-alloc/include/xo/alloc/GC.hpp index 1e73362e..abbb9657 100644 --- a/xo-alloc/include/xo/alloc/GC.hpp +++ b/xo-alloc/include/xo/alloc/GC.hpp @@ -54,6 +54,8 @@ namespace xo { bool allow_incremental_gc_ = true; /** true to report statistics **/ bool stats_flag_ = false; + /** true to capture per-type object statistics **/ + bool object_stats_flag_ = false; /** remember basic gc statistics for this many GC's; separately for incremental + full GCs **/ std::size_t stats_history_z_ = 256; /** true to enable debug logging **/ diff --git a/xo-alloc/include/xo/alloc/Object.hpp b/xo-alloc/include/xo/alloc/Object.hpp index 8d74f5af..5a981998 100644 --- a/xo-alloc/include/xo/alloc/Object.hpp +++ b/xo-alloc/include/xo/alloc/Object.hpp @@ -258,8 +258,9 @@ namespace xo { **/ class Cpof { public: - explicit Cpof(const Object * src) : src_{src} {} + explicit Cpof(gc::IAlloc * mm, const Object * src) : mm_{mm}, src_{src} {} + gc::IAlloc * mm_ = nullptr; const void * src_ = nullptr; }; } /*namespace xo*/ diff --git a/xo-alloc/src/alloc/ArenaAlloc.cpp b/xo-alloc/src/alloc/ArenaAlloc.cpp index 4fdcefe8..febbcb61 100644 --- a/xo-alloc/src/alloc/ArenaAlloc.cpp +++ b/xo-alloc/src/alloc/ArenaAlloc.cpp @@ -26,6 +26,11 @@ namespace xo { void * base = mmap(nullptr, z, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + log && log("acquired memory [lo,hi) using mmap", + xtag("lo", base), + xtag("z", z), + xtag("hi", reinterpret_cast(base) + z)); + // could use this as fallback.. //base = (new std::byte [z]); @@ -39,7 +44,7 @@ namespace xo { this->checkpoint_ = lo_; this->free_ptr_ = lo_; this->limit_ = lo_ + z; - this->hi_ = limit_; + this->hi_ = lo_ + z; this->debug_flag_ = debug_flag; if (!lo_) { @@ -52,22 +57,25 @@ namespace xo { ArenaAlloc::~ArenaAlloc() { + scope log(XO_DEBUG(debug_flag_)); // hygiene.. if (lo_) { + log && log("unmap [lo,hi)", xtag("lo", lo_), xtag("z", hi_ - lo_), xtag("hi", hi_)); + munmap(lo_, hi_ - lo_); } - // could use this as fallback if not using uncommitted technique + // could use this as fallback if we dropped the uncommitted technique //delete [] this->lo_; - this->lo_ = nullptr; + this->lo_ = nullptr; this->committed_z_ = 0; - this->checkpoint_ = nullptr; - this->free_ptr_ = nullptr; - this->limit_ = nullptr; - this->hi_ = nullptr; - this->debug_flag_ = false; + this->checkpoint_ = nullptr; + this->free_ptr_ = nullptr; + this->limit_ = nullptr; + this->hi_ = nullptr; + this->debug_flag_ = false; } up @@ -94,26 +102,41 @@ namespace xo { } bool - ArenaAlloc::expand(size_t offset_z) { + ArenaAlloc::expand(size_t offset_z) + { scope log(XO_DEBUG(debug_flag_), xtag("offset_z", offset_z), xtag("committed_z", committed_z_)); - if (offset_z <= committed_z_) + if (offset_z <= committed_z_) { + log && log("trivial success, offset within committed range", + xtag("offset_z", offset_z), + xtag("committed_z", committed_z_)); return true; - - std::size_t align_offset_z = align_lub(offset_z, page_z_); - std::byte * commit_start = lo_ + committed_z_; - std::size_t new_commit_z = align_offset_z - committed_z_; - - log && log(xtag("align_offset_z", align_offset_z), - xtag("new_commit_z", new_commit_z)); - - if (mprotect(commit_start, new_commit_z, PROT_READ | PROT_WRITE) != 0) { - throw std::runtime_error(tostr("ArenaAlloc::expand: commit failure", - xtag("committed_z", committed_z_), - xtag("new_commit_z", new_commit_z))); } - this->committed_z_ = align_offset_z; + if (lo_ + offset_z > limit_) { + throw std::runtime_error(tostr("ArenaAlloc::expand: requested size exceeds reserved size", + xtag("requested", offset_z), xtag("reserved", reserved()))); + } + + std::size_t aligned_offset_z = align_lub(offset_z, page_z_); + std::byte * commit_start = lo_ + committed_z_; + std::size_t add_commit_z = aligned_offset_z - committed_z_; + + log && log(xtag("aligned_offset_z", aligned_offset_z), + xtag("add_commit_z", add_commit_z)); + + log && log("expand committed range", + xtag("commit_start", commit_start), + xtag("add_commit_z", add_commit_z), + xtag("commit_end", commit_start + add_commit_z)); + + if (mprotect(commit_start, add_commit_z, PROT_READ | PROT_WRITE) != 0) { + throw std::runtime_error(tostr("ArenaAlloc::expand: commit failure", + xtag("committed_z", committed_z_), + xtag("add_commit_z", add_commit_z))); + } + + this->committed_z_ = aligned_offset_z; this->limit_ = this->lo_ + committed_z_; return true; @@ -167,6 +190,8 @@ namespace xo { std::byte * p = lo_; while (p < free_ptr_) { + log && log(xtag("p", (void *)p)); + Object * obj = reinterpret_cast(p); TaggedPtr tp = obj->self_tp(); std::size_t z = obj->_shallow_size(); diff --git a/xo-alloc/src/alloc/CMakeLists.txt b/xo-alloc/src/alloc/CMakeLists.txt index 5645b717..6bbc2f32 100644 --- a/xo-alloc/src/alloc/CMakeLists.txt +++ b/xo-alloc/src/alloc/CMakeLists.txt @@ -15,9 +15,9 @@ set(SELF_SRCS xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) # xo-unit used for time measurement -xo_dependency(${SELF_LIB} xo_unit) +xo_headeronly_dependency(${SELF_LIB} xo_unit) xo_dependency(${SELF_LIB} indentlog) xo_dependency(${SELF_LIB} reflect) -xo_dependency(${SELF_LIB} callback) +xo_headeronly_dependency(${SELF_LIB} callback) #end CMakeLists.txt diff --git a/xo-alloc/src/alloc/GC.cpp b/xo-alloc/src/alloc/GC.cpp index 051a68b3..8181cbb1 100644 --- a/xo-alloc/src/alloc/GC.cpp +++ b/xo-alloc/src/alloc/GC.cpp @@ -66,16 +66,36 @@ namespace xo { std::size_t nursery_size = config.initial_nursery_z_; std::size_t tenured_size = config.initial_tenured_z_; - nursery_[role2int(role::from_space)] + if (config_.incr_gc_threshold_ > nursery_size) { + throw std::runtime_error(tostr("GC::ctor: expected nursery gc threshold < nursery size", + xtag("nursery-gc-threshold", config_.incr_gc_threshold_), + xtag("nursery-size", nursery_size))); + } + + if (nursery_size + config_.full_gc_threshold_ > tenured_size) { + throw std::runtime_error(tostr("GC::ctor: expected nursery size + tennured gc threshold < tenured size", + xtag("nursery-size", nursery_size), + xtag("tenured-size", tenured_size), + xtag("full-gc-threshold", config_.full_gc_threshold_) + )); + } + + if (config_.incr_gc_threshold_ > nursery_size) + this->config_.incr_gc_threshold_ = nursery_size; + + if (config_.full_gc_threshold_ > tenured_size) + this->config_.full_gc_threshold_ = tenured_size; + + this->nursery_[role2int(role::from_space)] = ArenaAlloc::make("NA", nursery_size, config.debug_flag_); - nursery_[role2int(role::to_space) ] + this->nursery_[role2int(role::to_space) ] = ArenaAlloc::make("NB", nursery_size, config.debug_flag_); - tenured_[role2int(role::from_space)] + this->tenured_[role2int(role::from_space)] = ArenaAlloc::make("TA", tenured_size, config.debug_flag_); - tenured_[role2int(role::to_space) ] + this->tenured_[role2int(role::to_space) ] = ArenaAlloc::make("TB", tenured_size, config.debug_flag_); nursery_[role2int(role::from_space)]->expand(config.incr_gc_threshold_); @@ -83,9 +103,9 @@ namespace xo { tenured_[role2int(role::from_space)]->expand(config.full_gc_threshold_); tenured_[role2int(role::to_space) ]->expand(config.full_gc_threshold_); - mutation_log_[role2int(role::from_space)] = std::make_unique(); - mutation_log_[role2int(role::to_space )] = std::make_unique(); - defer_mutation_log_ = std::make_unique(); + this->mutation_log_[role2int(role::from_space)] = std::make_unique(); + this->mutation_log_[role2int(role::to_space )] = std::make_unique(); + this->defer_mutation_log_ = std::make_unique(); this->gc_history_ = CircularBuffer(config.stats_history_z_); @@ -96,23 +116,25 @@ namespace xo { /* hygiene */ this->clear(); - nursery_[role2int(role::from_space)].reset(); - nursery_[role2int(role::to_space) ].reset(); + this->nursery_[role2int(role::from_space)].reset(); + this->nursery_[role2int(role::to_space) ].reset(); - tenured_[role2int(role::from_space)].reset(); - tenured_[role2int(role::to_space) ].reset(); + this->tenured_[role2int(role::from_space)].reset(); + this->tenured_[role2int(role::to_space) ].reset(); - mutation_log_[role2int(role::from_space)].reset(); - mutation_log_[role2int(role::to_space) ].reset(); - defer_mutation_log_.reset(); + this->gc_root_v_.clear(); + + this->mutation_log_[role2int(role::from_space)].reset(); + this->mutation_log_[role2int(role::to_space) ].reset(); + this->defer_mutation_log_.reset(); } up GC::make(const Config & config) { - GC * gc = new GC(config); + //GC * gc = new GC(config); - return up{gc}; + return std::make_unique(config); } const std::string & @@ -608,16 +630,18 @@ namespace xo { void GC::capture_object_statistics(generation upto, capture_phase phase) { - /* scan nursery */ - this->nursery_[role2int(role::to_space)]->capture_object_statistics - (phase, - &object_statistics_sab_[gen2int(generation::nursery)]); - - if (upto == generation::tenured) { - /* scan tenured */ - this->tenured_[role2int(role::to_space)]->capture_object_statistics + if (config_.object_stats_flag_) { + /* scan nursery */ + this->nursery_[role2int(role::to_space)]->capture_object_statistics (phase, - &object_statistics_sab_[gen2int(generation::tenured)]); + &object_statistics_sab_[gen2int(generation::nursery)]); + + if (upto == generation::tenured) { + /* scan tenured */ + this->tenured_[role2int(role::to_space)]->capture_object_statistics + (phase, + &object_statistics_sab_[gen2int(generation::tenured)]); + } } } diff --git a/xo-alloc/src/alloc/Object.cpp b/xo-alloc/src/alloc/Object.cpp index b3db11ca..475d84ad 100644 --- a/xo-alloc/src/alloc/Object.cpp +++ b/xo-alloc/src/alloc/Object.cpp @@ -14,7 +14,7 @@ operator new (std::size_t z, const xo::Cpof & cpof) { using xo::gc::GC; - GC * gc = reinterpret_cast(xo::Object::mm); + GC * gc = reinterpret_cast(cpof.mm_); return gc->alloc_gc_copy(z, cpof.src_); } diff --git a/xo-alloc/utest/Forwarding1.test.cpp b/xo-alloc/utest/Forwarding1.test.cpp index e5dfb1fe..b39c35a1 100644 --- a/xo-alloc/utest/Forwarding1.test.cpp +++ b/xo-alloc/utest/Forwarding1.test.cpp @@ -33,7 +33,7 @@ namespace xo { void display(std::ostream & os) const final override { os << data_; } virtual std::size_t _shallow_size() const final override { return sizeof(*this); } - virtual Object * _shallow_copy() const final override { return new (Cpof(this)) DummyObject(*this); } + virtual Object * _shallow_copy() const final override { return new (Cpof(Object::mm, this)) DummyObject(*this); } virtual std::size_t _forward_children() final override { return _shallow_size(); } private: diff --git a/xo-imgui/example/ex2/imgui_ex2.cpp b/xo-imgui/example/ex2/imgui_ex2.cpp index 0b5aff1a..3da0c883 100644 --- a/xo-imgui/example/ex2/imgui_ex2.cpp +++ b/xo-imgui/example/ex2/imgui_ex2.cpp @@ -306,8 +306,8 @@ public: std::size_t tenured_tospace_scale() const; GcStateDescription snapshot_gc_state() const; - void generate_random_mutation(); - void generate_random_mutations(); + void generate_random_mutation(GC * gc); + void generate_random_mutations(GC * gc); public: int alloc_per_cycle_ = 1; @@ -415,10 +415,10 @@ AppState::snapshot_gc_state() const { } void -AppState::generate_random_mutation() { +AppState::generate_random_mutation(GC * gc) { if (rng_() % 1000 > (5 * 1000) / 7) { /* p=16% integer */ - gc_root_v_[next_root_++] = Integer::make(next_int_); + gc_root_v_[next_root_++] = Integer::make(gc, next_int_); } else if (rng_() % 1000 > (3 * 1000) / 7) { /* p=16% cons */ gp random_car = gc_root_v_.at(rng_() % gc_root_v_.size()); @@ -454,9 +454,9 @@ AppState::generate_random_mutation() { } void -AppState::generate_random_mutations() { +AppState::generate_random_mutations(GC * gc) { for (int i = 0; i < this->alloc_per_cycle_; ++i) { - this->generate_random_mutation(); + this->generate_random_mutation(gc); } } @@ -1921,7 +1921,7 @@ int main(int, char **) case draw_state_type::alloc: { /** generate random alloc **/ - app_state.generate_random_mutations(); + app_state.generate_random_mutations(app_state.gc_.get()); gcstate = app_state.snapshot_gc_state(); diff --git a/xo-interpreter/CMakeLists.txt b/xo-interpreter/CMakeLists.txt index 132e7b86..370a9650 100644 --- a/xo-interpreter/CMakeLists.txt +++ b/xo-interpreter/CMakeLists.txt @@ -20,13 +20,12 @@ add_definitions(${PROJECT_CXX_FLAGS}) # note on ordering: must read .cmake defn of lib before configuring any examples add_subdirectory(src/interpreter) - xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) # ---------------------------------------------------------------- #add_subdirectory(example) -#add_subdirectory(utest) +add_subdirectory(utest) # ---------------------------------------------------------------- diff --git a/xo-interpreter/cmake/xo-bootstrap-macros.cmake b/xo-interpreter/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..aba31169 --- /dev/null +++ b/xo-interpreter/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/xo-interpreter/cmake/xo_interpreterConfig.cmake.in b/xo-interpreter/cmake/xo_interpreterConfig.cmake.in new file mode 100644 index 00000000..1cf48ccd --- /dev/null +++ b/xo-interpreter/cmake/xo_interpreterConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(xo_alloc) +#find_dependency(xo_flatstring) +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/xo-interpreter/docs/CMakeLists.txt b/xo-interpreter/docs/CMakeLists.txt new file mode 100644 index 00000000..b9df2dd6 --- /dev/null +++ b/xo-interpreter/docs/CMakeLists.txt @@ -0,0 +1,9 @@ +# xo-alloc/docs/CMakeLists.txt + +xo_doxygen_collect_deps() +xo_docdir_doxygen_config() +xo_docdir_sphinx_config( + index.rst install.rst) + +# see xo-reader/doc or xo-unit/doc for working examples +# example.rst install.rst implementation.rst diff --git a/xo-interpreter/docs/conf.py b/xo-interpreter/docs/conf.py new file mode 100644 index 00000000..e5855955 --- /dev/null +++ b/xo-interpreter/docs/conf.py @@ -0,0 +1,39 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'xo interpreter documentation' +copyright = '2025, Roland Conybeare' +author = 'Roland Conybeare' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +#extensions = [] +extensions = [ "breathe", + "sphinx.ext.mathjax", # inline math + "sphinx.ext.autodoc", # generate info from docstrings + "sphinxcontrib.ditaa", # diagrams-through-ascii-art + "sphinxcontrib.plantuml" # text -> uml diagrams + ] + +# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in +# match project name in Doxyfile.in +breathe_default_project = "xodoxxml" + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +pygments_style = 'sphinx' + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +#html_theme = 'alabaster' +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] +html_favicon = '_static/img/favicon.ico' diff --git a/xo-interpreter/include/xo/interpreter/StackFrame.hpp b/xo-interpreter/include/xo/interpreter/StackFrame.hpp index c18f6bae..310229ca 100644 --- a/xo-interpreter/include/xo/interpreter/StackFrame.hpp +++ b/xo-interpreter/include/xo/interpreter/StackFrame.hpp @@ -1,6 +1,8 @@ /** @file StackFrame.hpp **/ +#include "xo/alloc/IAlloc.hpp" #include "xo/alloc/Object.hpp" +#include namespace xo { namespace scm { @@ -8,15 +10,59 @@ namespace xo { * @brief Represent a single runtime stack frame for a Schematika function * * StackFrame intended to be used for interpreted functions. - * Compiled functions will stil likely have stack frames, but need not use the + * Compiled functions will still likely have stack frames, but need not use the * @ref StackFrame class + * + * memory layout: + * + * +------------+ + * | vtable | + * +------------+ + * | .n_ | + * +------------+ + * | .v_ +------\ + * +------------+ <--/ + * | .v_[0] | + * +------------+ + * . .. . + * +------------+ + * | .v_[.n_-1] | + * +------------+ **/ class StackFrame : public Object { public: - StackFrame(std::size_t n_slot) + using TaggedPtr = xo::reflect::TaggedPtr; + + public: + StackFrame(gc::IAlloc * mm, std::size_t n_slot); + + /** create frame using allocator @p mm, + * with exactly @p n_slot object pointers + **/ + static gp make(gc::IAlloc * mm, std::size_t n_slot); + + /** reflect StackFrame object representation **/ + static void reflect_self(); + + std::size_t n_slot() const { return n_slot_; } + gp lookup(std::size_t i) const { return v_[i]; } + gp & lookup(std::size_t i) { return v_[i]; } + + gp operator[](std::size_t i) const { return lookup(i); } + gp & operator[](std::size_t i) { return lookup(i); } + + // inherited from Object.. + virtual TaggedPtr self_tp() const final override; + virtual void display(std::ostream & os) const final override; + virtual std::size_t _shallow_size() const final override; + virtual Object * _shallow_copy() const final override; + virtual std::size_t _forward_children() final override; private: - + /** number of elements in frame **/ + std::size_t n_slot_ = 0; + /** contiguous array of object pointers: v[0] .. v[n-1] **/ + gp * v_ = nullptr; }; } /*namespace scm*/ } /*namespace xo*/ diff --git a/xo-interpreter/include/xo/interpreter/init_interpreter.hpp b/xo-interpreter/include/xo/interpreter/init_interpreter.hpp new file mode 100644 index 00000000..91e4828c --- /dev/null +++ b/xo-interpreter/include/xo/interpreter/init_interpreter.hpp @@ -0,0 +1,21 @@ +/** @file init_interpreter.hpp + * + * author: Roland Conybeare, Nov 2025 + **/ + +#pragma once + +#include "xo/subsys/Subsystem.hpp" + +namespace xo { + /* tag to represent the interpreter/ subsystem in ordered initialization */ + enum S_interpreter_tag {}; + + template<> + struct InitSubsys { + static void init(); + static InitEvidence require(); + }; +} + +/* end init_interpreter.hpp */ diff --git a/xo-interpreter/src/interpreter/CMakeLists.txt b/xo-interpreter/src/interpreter/CMakeLists.txt new file mode 100644 index 00000000..d0824e34 --- /dev/null +++ b/xo-interpreter/src/interpreter/CMakeLists.txt @@ -0,0 +1,13 @@ +# interpreter/CMakeLists.txt + +set(SELF_LIB xo_interpreter) +set(SELF_SRCS + init_interpreter.cpp + StackFrame.cpp + VirtualSchematikaMachine.cpp +) + +xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_dependency(${SELF_LIB} xo_alloc) +#xo_dependency(${SELF_LIB} xo_reader) +xo_headeronly_dependency(${SELF_LIB} subsys) diff --git a/xo-interpreter/src/interpreter/StackFrame.cpp b/xo-interpreter/src/interpreter/StackFrame.cpp new file mode 100644 index 00000000..dcfec083 --- /dev/null +++ b/xo-interpreter/src/interpreter/StackFrame.cpp @@ -0,0 +1,120 @@ +/** @file StackFrame.cpp **/ + +#include "StackFrame.hpp" +#include "xo/reflect/Reflect.hpp" +#include "xo/reflect/StructReflector.hpp" +#include + +namespace xo { + using xo::reflect::Reflect; + using xo::reflect::StructReflector; + using xo::reflect::TaggedPtr; + using xo::print::quot; + + namespace scm { + namespace { + std::size_t + slot_array_size(std::size_t n) { + return n * sizeof(gp); + } + } + + StackFrame::StackFrame(gc::IAlloc * mm, std::size_t n_slot) + : n_slot_{n_slot}, v_{nullptr} + { + if (n_slot > 0) { + std::byte * mem = mm->alloc(slot_array_size(n_slot)); + + this->v_ = new (mem) gp[n_slot]; + } + } + + gp + StackFrame::make(gc::IAlloc * mm, std::size_t n_slot) + { + return new (MMPtr(mm)) StackFrame(mm, n_slot); + } + + TaggedPtr + StackFrame::self_tp() const + { + return Reflect::make_tp(const_cast(this)); + } + + void + StackFrame::display(std::ostream & os) const + { + os << ""; + } + + std::size_t + StackFrame::_shallow_size() const + { + std::size_t retval = sizeof(StackFrame); + + retval += gc::IAlloc::with_padding(slot_array_size(n_slot_)); + + return retval; + } + + Object * + StackFrame::_shallow_copy() const + { + Cpof cpof(Object::mm, this); + + StackFrame * copy = new (cpof) StackFrame(cpof.mm_, n_slot_); + + void * v_dest = copy->v_; + + if (v_) { + ::memcpy(v_dest, v_, slot_array_size(n_slot_)); + } + +#ifdef OBSOLETE + for (size_t i = 0, n = n_slot_; i < n; ++i) { + copy->v_[i] = v_[i]; + } +#endif + + return copy; + } + + std::size_t + StackFrame::_forward_children() + { + for (std::size_t i = 0, n = n_slot_; i < n; ++i) { + Object::_forward_inplace(lookup(i)); + } + + return _shallow_size(); + } + + void + StackFrame::reflect_self() + { + StructReflector sr; + + if (sr.is_incomplete()) { + REFLECT_MEMBER(sr, n_slot); + + // non-trivial to reflect frame members, + // effectively need separate reflection for each cardinality; + // or: reflect .v_[] as nested element + } + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end StackFrame.cpp */ diff --git a/xo-interpreter/src/interpreter/init_interpreter.cpp b/xo-interpreter/src/interpreter/init_interpreter.cpp new file mode 100644 index 00000000..3f6565ff --- /dev/null +++ b/xo-interpreter/src/interpreter/init_interpreter.cpp @@ -0,0 +1,27 @@ +/** @file init_interpreter.cpp + * + * author: Roland Conybeare, Nov 2025 + */ + +#include "init_interpreter.hpp" +#include "StackFrame.hpp" +#include "xo/subsys/Subsystem.hpp" + +namespace xo { + using xo::scm::StackFrame; + + void + InitSubsys::init() + { + StackFrame::reflect_self(); + } + + InitEvidence + InitSubsys::require() + { + return Subsystem::provide("interpreter", &init); + } + +} /*namespace xo*/ + +/* end init_interpreter.cpp */ diff --git a/xo-interpreter/utest/CMakeLists.txt b/xo-interpreter/utest/CMakeLists.txt new file mode 100644 index 00000000..5eebba74 --- /dev/null +++ b/xo-interpreter/utest/CMakeLists.txt @@ -0,0 +1,12 @@ +# build unittest interpreter/utest + +set(UTEST_EXE utest.interpreter) +set(UTEST_SRCS + interpreter_utest_main.cpp + StackFrame.test.cpp +) + +xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS}) +xo_self_dependency(${UTEST_EXE} xo_interpreter) +xo_dependency(${UTEST_EXE} xo_object) +xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2) diff --git a/xo-interpreter/utest/StackFrame.test.cpp b/xo-interpreter/utest/StackFrame.test.cpp new file mode 100644 index 00000000..5a64c248 --- /dev/null +++ b/xo-interpreter/utest/StackFrame.test.cpp @@ -0,0 +1,133 @@ +/** @file StackFrame.test.cpp **/ + +#include "xo/interpreter/init_interpreter.hpp" +#include "xo/interpreter/StackFrame.hpp" +#include "xo/object/Integer.hpp" +#include "xo/alloc/GC.hpp" +#include +#include +#include + +namespace xo { + using xo::scm::StackFrame; + using xo::obj::Integer; + using xo::gc::GC; + using xo::gc::ArenaAlloc; + using xo::gc::generation; + using xo::gc::generation_result; + using xo::reflect::TaggedPtr; + + namespace ut { + static InitEvidence s_init = (InitSubsys::require()); + + namespace { + struct Testcase_StackFrame { + Testcase_StackFrame(const std::vector & contents) : contents_{contents} {} + + /* build xo::obj::Integer for each contents_[i], store in F[i] for new StackFrame F */ + std::vector contents_; + }; + + std::vector + s_testcase_v = { + Testcase_StackFrame({}), + Testcase_StackFrame({}), + Testcase_StackFrame({111}), + }; + } + + TEST_CASE("StackFrame", "[StackFrame][interpreter]") + { + Subsystem::initialize_all(); + + constexpr bool c_debug_flag = false; + + for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) { + scope log(XO_DEBUG(c_debug_flag), xtag("test", "StackFrame2"), xtag("i_tc", i_tc)); + + const Testcase_StackFrame & tc = s_testcase_v[i_tc]; + + up alloc = ArenaAlloc::make("utest", 16384, c_debug_flag); + REQUIRE(alloc.get()); + Object::mm = alloc.get(); + + std::size_t n = tc.contents_.size(); + gp frame = StackFrame::make(alloc.get(), n); + + TaggedPtr tp = frame->self_tp(); + + REQUIRE(tp.is_struct()); + } + } + + TEST_CASE("StackFrame2", "[StackFrame][gc][interpreter]") + { + Subsystem::initialize_all(); + + constexpr bool c_debug_flag = false; + + try { + for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) { + scope log(XO_DEBUG(c_debug_flag), xtag("test", "StackFrame2"), xtag("i_tc", i_tc)); + + const Testcase_StackFrame & tc = s_testcase_v[i_tc]; + + up gc = GC::make( + {.initial_nursery_z_ = 16384, + .initial_tenured_z_ = 32768, + .incr_gc_threshold_ = 4096, + .full_gc_threshold_ = 4096, + .object_stats_flag_ = true, + .debug_flag_ = c_debug_flag, + }); + + REQUIRE(gc.get()); + + /* use gc for all Object allocs */ + GC * mm = gc.get(); + Object::mm = mm; + + std::size_t n = tc.contents_.size(); + + gp x = Integer::make(gc.get(), 42); + gc->add_gc_root(reinterpret_cast(&x)); + REQUIRE(gc->tospace_generation_of(x.ptr()) == generation_result::nursery); + + gp frame = StackFrame::make(gc.get(), n); + StackFrame ** frame_pp = frame.ptr_address(); + gc->add_gc_root(reinterpret_cast(frame_pp)); + + /* verifying allocated in N1 */ + REQUIRE(gc->tospace_generation_of(frame.ptr()) == generation_result::nursery); + + for (std::size_t i = 0; i < n; ++i) + frame->lookup(i) = Integer::make(mm, tc.contents_.at(i)); + + std::size_t expected_alloc_z = frame->_shallow_size(); + REQUIRE(expected_alloc_z >= sizeof(StackFrame) + n * sizeof(gp)); + + gc->request_gc(generation::nursery); // <<<<<<<<< GC here <<<<<<<<< + + REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1); + REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0); + + /* verify Integer x preserved across gc */ + REQUIRE(gc->tospace_generation_of(x.ptr()) == generation_result::nursery); + + /* verify StackFrame preserved across gc */ + REQUIRE(gc->tospace_generation_of(frame.ptr()) == generation_result::nursery); + REQUIRE(frame->n_slot() == n); + for (std::size_t i = 0; i < n; ++i) { + //REQUIRE(Integer::from(frame->lookup(i)).ptr()); + //REQUIRE(Integer::from(frame->lookup(i))->value() == tc.contents_.at(i)); + } + } + } catch (std::exception & ex) { + std::cerr << "exception: " << ex.what() << std::endl; + REQUIRE(false); + } + } + } +} + +/* end StackFrame.test.cpp */ diff --git a/xo-interpreter/utest/interpreter_utest_main.cpp b/xo-interpreter/utest/interpreter_utest_main.cpp new file mode 100644 index 00000000..e385f9b4 --- /dev/null +++ b/xo-interpreter/utest/interpreter_utest_main.cpp @@ -0,0 +1,6 @@ +/** @file interpreter_utest_main.cpp **/ + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" + +/* end interpreter_utest_main.cpp */ diff --git a/xo-jit/utest/CMakeLists.txt b/xo-jit/utest/CMakeLists.txt index 00614ddd..b0193a74 100644 --- a/xo-jit/utest/CMakeLists.txt +++ b/xo-jit/utest/CMakeLists.txt @@ -10,7 +10,7 @@ if (ENABLE_TESTING) xo_add_utest_executable(${SELF_EXE} ${SELF_SRCS}) xo_self_dependency(${SELF_EXE} xo_jit) xo_dependency(${SELF_EXE} xo_ratio) - xo_dependency(${SELF_EXE} xo_reflectutil) + xo_headeronly_dependency(${SELF_EXE} xo_reflectutil) xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2) endif() diff --git a/xo-object/include/xo/object/Integer.hpp b/xo-object/include/xo/object/Integer.hpp index 05193023..5f3a2d86 100644 --- a/xo-object/include/xo/object/Integer.hpp +++ b/xo-object/include/xo/object/Integer.hpp @@ -11,6 +11,7 @@ namespace xo { namespace obj { class Integer : public Number { public: + using IAlloc = xo::gc::IAlloc; using int_type = long long; public: @@ -18,7 +19,7 @@ namespace xo { explicit Integer(int_type x); /** create instance holding integer value @p x **/ - static gp make(int_type x); + static gp make(IAlloc * mm, int_type x); /** downcast from @p x iff x is actually an Integer. Otherwise nullptr **/ static gp from(gp x); diff --git a/xo-object/src/object/CMakeLists.txt b/xo-object/src/object/CMakeLists.txt index dedabedf..eee16738 100644 --- a/xo-object/src/object/CMakeLists.txt +++ b/xo-object/src/object/CMakeLists.txt @@ -8,4 +8,8 @@ set(SELF_SRCS Integer.cpp) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_headeronly_dependency(${SELF_LIB} xo_reflectutil) +xo_headeronly_dependency(${SELF_LIB} xo_unit) +xo_headeronly_dependency(${SELF_LIB} callback) xo_dependency(${SELF_LIB} xo_alloc) +xo_dependency(${SELF_LIB} reflect) diff --git a/xo-object/src/object/Integer.cpp b/xo-object/src/object/Integer.cpp index 512cb245..59775de6 100644 --- a/xo-object/src/object/Integer.cpp +++ b/xo-object/src/object/Integer.cpp @@ -10,12 +10,13 @@ namespace xo { using xo::reflect::Reflect; using xo::reflect::TaggedPtr; + using xo::gc::IAlloc; namespace obj { Integer::Integer(int_type x) : value_{x} {} gp - Integer::make(int_type x) { + Integer::make(IAlloc * mm, int_type x) { return new (MMPtr(mm)) Integer(x); } @@ -41,7 +42,7 @@ namespace xo { Object * Integer::_shallow_copy() const { - Cpof cpof(this); + Cpof cpof(Object::mm, this); return new (cpof) Integer(*this); } diff --git a/xo-object/src/object/List.cpp b/xo-object/src/object/List.cpp index ae037915..91674920 100644 --- a/xo-object/src/object/List.cpp +++ b/xo-object/src/object/List.cpp @@ -102,7 +102,7 @@ namespace xo { assert(!(this->is_nil())); - Cpof cpof(this); + Cpof cpof(Object::mm, this); return new (cpof) List(*this); } diff --git a/xo-object/src/object/String.cpp b/xo-object/src/object/String.cpp index b4d3c02c..c5de286e 100644 --- a/xo-object/src/object/String.cpp +++ b/xo-object/src/object/String.cpp @@ -130,7 +130,7 @@ namespace xo { { // Reminder: String must come before secondary allocation, - Cpof cpof(this); + Cpof cpof(Object::mm, this); // might expect to write: // gp copy = new (gcm) String(Object::mm, owner_, z_chars_, chars_); diff --git a/xo-object/utest/GC.test.cpp b/xo-object/utest/GC.test.cpp index b139f4a4..843b67a6 100644 --- a/xo-object/utest/GC.test.cpp +++ b/xo-object/utest/GC.test.cpp @@ -55,10 +55,10 @@ namespace xo { /* use gc for all Object allocs */ Object::mm = gc.get(); - gp l = List::list(Integer::make(1)); + gp l = List::list(Integer::make(gc.get(), 1)); gc->add_gc_root(reinterpret_cast(l.ptr_address())); - gp l2 = List::list(Integer::make(10)); + gp l2 = List::list(Integer::make(gc.get(), 10)); gc->add_gc_root(reinterpret_cast(l2.ptr_address())); { @@ -80,7 +80,7 @@ namespace xo { // mutation, but not {xgen, xckp} since parent,child both in N0 - l->assign_head(Integer::make(2)); + l->assign_head(Integer::make(gc.get(), 2)); { REQUIRE(gc->native_gc_statistics().n_mutation_ == 1); REQUIRE(gc->native_gc_statistics().n_logged_mutation_ == 0); @@ -106,7 +106,7 @@ namespace xo { // mutation, xckp since parent in N1, child in N0 - l->assign_head(Integer::make(3)); + l->assign_head(Integer::make(gc.get(), 3)); { REQUIRE(Integer::from(l->head())->value() == 3); @@ -373,11 +373,11 @@ namespace xo { RandomMutationModel(std::size_t m, std::size_t n, std::size_t r, std::size_t rr, std::size_t k) : m_{m}, n_{n}, r_{r}, rr_{rr}, k_{k} {} - void generate_seed_values(); + void generate_seed_values(GC * gc); void generate_random_roots(GC * gc, xoshiro256ss * p_rgen); void generate_random_mutations(xoshiro256ss * p_rgen); - void rejuvenate_seed_values(); + void rejuvenate_seed_values(GC * gc); void alter_random_roots(xoshiro256ss * p_rgen); /* create m random list cells */ @@ -400,7 +400,7 @@ namespace xo { std::vector> root_v_; }; - void RandomMutationModel::generate_seed_values() + void RandomMutationModel::generate_seed_values(GC * gc) { w1_.clear(); w2_.clear(); @@ -415,7 +415,7 @@ namespace xo { { std::copy(w1_.begin(), w1_.end(), std::back_inserter(w2_)); for (size_t j = 0; j < n_; ++j) { - w2_.push_back(Integer::make((this->start_)++)); + w2_.push_back(Integer::make(gc, (this->start_)++)); } REQUIRE(w2_.size() == m_ + n_); } @@ -468,7 +468,7 @@ namespace xo { } } - void RandomMutationModel::rejuvenate_seed_values() + void RandomMutationModel::rejuvenate_seed_values(GC * gc) { for (std::size_t i = 0; i < w1_.size(); ++i) { INFO(xtag("i", i)); @@ -492,7 +492,7 @@ namespace xo { REQUIRE(w2_[j].ptr()); } else { /* w2[j] is garbage, replace */ - w2_[j] = Integer::make((this->start_)++); + w2_[j] = Integer::make(gc, (this->start_)++); REQUIRE(w2_[j].ptr()); } } @@ -616,7 +616,7 @@ namespace xo { INFO(xtag("cycle", cycle)); if (cycle == 0) { - data_model.generate_seed_values(); + data_model.generate_seed_values(gc.get()); data_model.generate_random_roots(gc.get(), &rgen); } else { /* figure out values in {data_model_.w1_, data_model_.w2_} that @@ -626,7 +626,7 @@ namespace xo { * (For example want to verify behavior of GC w.r.t. cells that are alive only * because of a mutation) */ - data_model.rejuvenate_seed_values(); + data_model.rejuvenate_seed_values(gc.get()); data_model.alter_random_roots(&rgen); } diff --git a/xo-object/utest/Integer.test.cpp b/xo-object/utest/Integer.test.cpp index 94a4e4e2..59db1738 100644 --- a/xo-object/utest/Integer.test.cpp +++ b/xo-object/utest/Integer.test.cpp @@ -24,8 +24,8 @@ namespace xo { Object::mm = gc.get(); - gp i1 = Integer::make(123); - gp i2 = Integer::make(-321); + gp i1 = Integer::make(gc.get(), 123); + gp i2 = Integer::make(gc.get(), -321); REQUIRE(i1->value() == 123); REQUIRE(i2->value() == -321); diff --git a/xo-object/utest/List.test.cpp b/xo-object/utest/List.test.cpp index 925c7a7c..8cb128c4 100644 --- a/xo-object/utest/List.test.cpp +++ b/xo-object/utest/List.test.cpp @@ -289,9 +289,10 @@ namespace xo { up alloc = ArenaAlloc::make("arena", 1024, c_debug_flag); - Object::mm = alloc.get(); + ArenaAlloc * mm = alloc.get(); + Object::mm = mm; - gp l = List::list(Integer::make(1), Integer::make(2), Integer::make(3)); + gp l = List::list(Integer::make(mm, 1), Integer::make(mm, 2), Integer::make(mm, 3)); REQUIRE(l->size() == 3); diff --git a/xo-ratio/utest/CMakeLists.txt b/xo-ratio/utest/CMakeLists.txt index c1dbfaa5..f1ebac58 100644 --- a/xo-ratio/utest/CMakeLists.txt +++ b/xo-ratio/utest/CMakeLists.txt @@ -17,7 +17,7 @@ if (ENABLE_TESTING) xo_self_headeronly_dependency(${SELF_EXE} xo_ratio) xo_dependency(${SELF_EXE} reflect) # need explicit header-only dependencies, at least in submodule build - xo_dependency(${SELF_EXE} xo_reflectutil) + xo_headeronly_dependency(${SELF_EXE} xo_reflectutil) xo_dependency(${SELF_EXE} randomgen) xo_dependency(${SELF_EXE} indentlog) xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2) diff --git a/xo-reader/docs/install.rst b/xo-reader/docs/install.rst index 0860150f..0f008815 100644 --- a/xo-reader/docs/install.rst +++ b/xo-reader/docs/install.rst @@ -24,12 +24,13 @@ Install One-step Install ---------------- -Install along with the rest of *XO* from `xo-umbrella2 source`_ +Install along with the rest of *XO* from `xo-umbrella2 source`_: +see install instructions for xo-umrbella2 .. _xo-umbrella2 source: https://github.com/rconybea/xo-umbrella2 -Minimal Dependencies --------------------- +Essential Xo Dependencies +------------------------- ``xo-reader`` uses several supporting libraries from the *XO* project: diff --git a/xo-reflect/include/xo/reflect/EstablishTypeDescr.hpp b/xo-reflect/include/xo/reflect/EstablishTypeDescr.hpp index 0f18d84a..e461a91a 100644 --- a/xo-reflect/include/xo/reflect/EstablishTypeDescr.hpp +++ b/xo-reflect/include/xo/reflect/EstablishTypeDescr.hpp @@ -39,10 +39,10 @@ namespace xo { template static TypeDescrW establish() { - TypeDescrW td = TypeDescrBase::require(&typeid(T), - std::string(type_name()), - nullptr /*tdextra*/, - nullptr /*invoker*/); + static TypeDescrW td = TypeDescrBase::require(&typeid(T), + std::string(type_name()), + nullptr /*tdextra*/, + nullptr /*invoker*/); #ifdef NOT_USING std::function to_self_tp; diff --git a/xo-reflect/src/reflect/CMakeLists.txt b/xo-reflect/src/reflect/CMakeLists.txt index 87417066..c9a95d66 100644 --- a/xo-reflect/src/reflect/CMakeLists.txt +++ b/xo-reflect/src/reflect/CMakeLists.txt @@ -13,7 +13,7 @@ set(SELF_SRCS xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) xo_dependency(${SELF_LIB} refcnt) xo_dependency(${SELF_LIB} indentlog) -xo_dependency(${SELF_LIB} subsys) +xo_headeronly_dependency(${SELF_LIB} subsys) #xo_boost_dependency(${SELF_LIB}) # end CMakeLists.txt diff --git a/xo-reflect/src/reflect/TypeDescr.cpp b/xo-reflect/src/reflect/TypeDescr.cpp index 676225f2..de5f4ada 100644 --- a/xo-reflect/src/reflect/TypeDescr.cpp +++ b/xo-reflect/src/reflect/TypeDescr.cpp @@ -57,6 +57,10 @@ namespace xo { detail::Invoker * invoker, std::unique_ptr tdextra) { + scope log(XO_DEBUG(false)); + + log && log(xtag("canonical_name", canonical_name)); + if (native_tinfo) { /* 1. lookup by tinfo hash_code in s_type_table_map * Not available for manually-constructed type descriptions. @@ -64,19 +68,29 @@ namespace xo { { auto ix = s_native_type_table_map.find(TypeInfoRef(native_tinfo)); - if ((ix != s_native_type_table_map.end()) && ix->second) + if ((ix != s_native_type_table_map.end()) && ix->second) { + log && log("TypeDescrBase::require" + ": using s_native_type_table_map[TypeInfoRef(native_tinfo)]"); + return ix->second; + } } /* 2. lookup by tinfo hash_code in s_coalesced_type_table_map */ { auto ix = s_coalesced_type_table_map.find(TypeInfoRef(native_tinfo)); - if ((ix != s_coalesced_type_table_map.end()) && ix->second) + if ((ix != s_coalesced_type_table_map.end()) && ix->second) { + log && log("TypeDescrBase::require" + ": using s_coalesced_type_table_map[TypeInfoRef(native_tinfo)]"); + return ix->second; + } } } + log && log("TypeDescrBase::require: try lookup by canonical name"); + /* 3. lookup by canonical_name, before we create a new slot. * * Have to accept that on clang type_info objects aren't always unique (!$@#!!) @@ -85,6 +99,7 @@ namespace xo { auto ix = s_canonical_type_table_map.find(canonical_name); if (ix != s_canonical_type_table_map.end()) { + /** assume existing slot, with same canonical name, * represents the same type as native_tinfo **/ @@ -145,6 +160,8 @@ namespace xo { TypeId new_td_id = TypeId::allocate(); + log && log("TypeDescrBase::require", xtag("new_td_id", new_td_id)); + if (s_type_table_v.size() <= new_td_id.id()) s_type_table_v.resize(new_td_id.id() + 1); diff --git a/xo-reflect/src/reflect/init_reflect.cpp b/xo-reflect/src/reflect/init_reflect.cpp index d95bf2e3..bcfa34be 100644 --- a/xo-reflect/src/reflect/init_reflect.cpp +++ b/xo-reflect/src/reflect/init_reflect.cpp @@ -7,17 +7,17 @@ #include "xo/subsys/Subsystem.hpp" namespace xo { - void - InitSubsys::init() - { - /* placeholder -- expecting there to be non-trivial content soon */ - } /*init*/ + void + InitSubsys::init() + { + /* placeholder -- expecting there to be non-trivial content soon */ + } /*init*/ - InitEvidence - InitSubsys::require() - { - return Subsystem::provide("reflect", &init); - } /*require*/ + InitEvidence + InitSubsys::require() + { + return Subsystem::provide("reflect", &init); + } /*require*/ } /*namespace xo*/ /* end init_reflect.cpp */