From c3aa9ee6d5a2bc4c04f9d73280898fb64fadb12b Mon Sep 17 00:00:00 2001 From: leftibot Date: Thu, 9 Apr 2026 22:50:28 -0600 Subject: [PATCH] Fix #146: Add configuration options to selectively disable built-in functions Add new Options enum values (No_Stdlib, No_IO, No_Prelude, No_JSON) that allow users to control which parts of the standard library are registered. Std_Lib::library() now accepts an options vector, and the ChaiScript convenience class forwards its options to the library builder. This enables use cases where only custom functions should be exposed to script users. Co-Authored-By: Claude Opus 4.6 (1M context) --- include/chaiscript/chaiscript.hpp | 2 +- include/chaiscript/chaiscript_defines.hpp | 6 +- include/chaiscript/chaiscript_stdlib.hpp | 21 ++++- include/chaiscript/dispatchkit/bootstrap.hpp | 10 ++- unittests/compiled_tests.cpp | 90 ++++++++++++++++++++ 5 files changed, 119 insertions(+), 10 deletions(-) diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 871b353b..647ed5b6 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -825,7 +825,7 @@ namespace chaiscript { ChaiScript(std::vector t_modulepaths = {}, std::vector t_usepaths = {}, std::vector t_opts = chaiscript::default_options()) - : ChaiScript_Basic(chaiscript::Std_Lib::library(), + : ChaiScript_Basic(chaiscript::Std_Lib::library(t_opts), std::make_unique>(), std::move(t_modulepaths), std::move(t_usepaths), diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index e01aadd3..fb07cf28 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -208,7 +208,11 @@ namespace chaiscript { No_Load_Modules, Load_Modules, No_External_Scripts, - External_Scripts + External_Scripts, + No_Stdlib, + No_IO, + No_Prelude, + No_JSON }; template diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index 5cb4fa68..bcdc3165 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -38,9 +38,18 @@ namespace chaiscript { class Std_Lib { public: - [[nodiscard]] static ModulePtr library() { + [[nodiscard]] static ModulePtr library(const std::vector &t_opts = {}) { + if (std::find(t_opts.begin(), t_opts.end(), Options::No_Stdlib) != t_opts.end()) { + return std::make_shared(); + } + auto lib = std::make_shared(); - bootstrap::Bootstrap::bootstrap(*lib); + + const bool no_io = std::find(t_opts.begin(), t_opts.end(), Options::No_IO) != t_opts.end(); + const bool no_prelude = std::find(t_opts.begin(), t_opts.end(), Options::No_Prelude) != t_opts.end(); + const bool no_json = std::find(t_opts.begin(), t_opts.end(), Options::No_JSON) != t_opts.end(); + + bootstrap::Bootstrap::bootstrap(*lib, no_io); bootstrap::standard_library::vector_type>("Vector", *lib); bootstrap::standard_library::string_type("string", *lib); @@ -54,9 +63,13 @@ namespace chaiscript { "async"); #endif - json_wrap::library(*lib); + if (!no_json) { + json_wrap::library(*lib); + } - lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/); + if (!no_prelude) { + lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/); + } return lib; } diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index ba1de5ad..03ad1203 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -266,8 +266,8 @@ namespace chaiscript::bootstrap { public: /// \brief perform all common bootstrap functions for std::string, void and POD types /// \param[in,out] m Module to add bootstrapped functions to - /// \returns passed in Module - static void bootstrap(Module &m) { + /// \param[in] t_no_io If true, skip registering print_string and println_string + static void bootstrap(Module &m, const bool t_no_io = false) { m.add(user_type(), "void"); m.add(user_type(), "bool"); m.add(user_type(), "Object"); @@ -435,8 +435,10 @@ namespace chaiscript::bootstrap { m.add(fun(&Build_Info::compiler_id), "compiler_id"); m.add(fun(&Build_Info::debug_build), "debug_build"); - m.add(fun(&print), "print_string"); - m.add(fun(&println), "println_string"); + if (!t_no_io) { + m.add(fun(&print), "print_string"); + m.add(fun(&println), "println_string"); + } m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 1462c203..8cda152c 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1283,3 +1283,93 @@ TEST_CASE("Test if non copyable/movable types can be registered") { chai.add(chaiscript::user_type(), "Nothing"); chai.add(chaiscript::constructor(), "Nothing"); } + +// Tests for issue #146: configuration to bypass registering built-in functions + +TEST_CASE("No_Stdlib option disables all standard library functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Options::No_Stdlib}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + // Core types should still work (they are part of bootstrap) + CHECK_NOTHROW(chai.eval("var x = 5")); + + // print should not exist + CHECK_THROWS(chai.eval("print(\"hello\")")); + + // Vector should not exist + CHECK_THROWS(chai.eval("var v = Vector()")); + + // String methods from prelude should not exist + CHECK_THROWS(chai.eval("\"hello\".trim()")); + + // JSON should not exist + CHECK_THROWS(chai.eval("from_json(\"[1,2,3]\")")); +} + +TEST_CASE("No_IO option disables print functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Options::No_IO}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + // print_string and println_string should not exist + CHECK_THROWS(chai.eval("print_string(\"hello\")")); + CHECK_THROWS(chai.eval("println_string(\"hello\")")); + + // Other functionality should still work + CHECK(chai.eval("5 + 3") == 8); + CHECK_NOTHROW(chai.eval("var v = Vector()")); +} + +TEST_CASE("No_Prelude option disables prelude functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Options::No_Prelude}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + // print (from prelude) should not exist + CHECK_THROWS(chai.eval("print(\"hello\")")); + + // But print_string (from bootstrap) should still work + CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); + + // Prelude utility functions should not exist + CHECK_THROWS(chai.eval("filter([1,2,3], fun(x) { x > 1 })")); + + // Core arithmetic should still work + CHECK(chai.eval("5 + 3") == 8); +} + +TEST_CASE("No_JSON option disables JSON support") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Options::No_JSON}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + // JSON functions should not exist + CHECK_THROWS(chai.eval("from_json(\"[1,2,3]\")")); + + // Other functionality should still work + CHECK(chai.eval("5 + 3") == 8); + CHECK_NOTHROW(chai.eval("print(\"hello\")")); +} + +TEST_CASE("Default library has all functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + // Everything should work with default options + CHECK_NOTHROW(chai.eval("print(\"hello\")")); + CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); + CHECK_NOTHROW(chai.eval("var v = Vector()")); + CHECK(chai.eval("5 + 3") == 8); +}