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) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-09 22:50:28 -06:00
parent 2eb3279c39
commit c3aa9ee6d5
5 changed files with 119 additions and 10 deletions

View File

@ -825,7 +825,7 @@ namespace chaiscript {
ChaiScript(std::vector<std::string> t_modulepaths = {},
std::vector<std::string> t_usepaths = {},
std::vector<Options> t_opts = chaiscript::default_options())
: ChaiScript_Basic(chaiscript::Std_Lib::library(),
: ChaiScript_Basic(chaiscript::Std_Lib::library(t_opts),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>(),
std::move(t_modulepaths),
std::move(t_usepaths),

View File

@ -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<typename From, typename To>

View File

@ -38,9 +38,18 @@
namespace chaiscript {
class Std_Lib {
public:
[[nodiscard]] static ModulePtr library() {
[[nodiscard]] static ModulePtr library(const std::vector<Options> &t_opts = {}) {
if (std::find(t_opts.begin(), t_opts.end(), Options::No_Stdlib) != t_opts.end()) {
return std::make_shared<Module>();
}
auto lib = std::make_shared<Module>();
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<std::vector<Boxed_Value>>("Vector", *lib);
bootstrap::standard_library::string_type<std::string>("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;
}

View File

@ -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>(), "void");
m.add(user_type<bool>(), "bool");
m.add(user_type<Boxed_Value>(), "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");

View File

@ -1283,3 +1283,93 @@ TEST_CASE("Test if non copyable/movable types can be registered") {
chai.add(chaiscript::user_type<Nothing>(), "Nothing");
chai.add(chaiscript::constructor<Nothing()>(), "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<int>("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<int>("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<int>("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<int>("5 + 3") == 8);
}