34 {.name =
"", .open = luaopen_base},
35 {.name = LUA_COLIBNAME, .open = luaopen_coroutine},
36 {.name = LUA_TABLIBNAME, .open = luaopen_table},
37 {.name = LUA_STRLIBNAME, .open = luaopen_string},
38 {.name = LUA_MATHLIBNAME, .open = luaopen_math},
39 {.name = LUA_UTF8LIBNAME, .open = luaopen_utf8},
40 {.name = LUA_BITLIBNAME, .open = luaopen_bit32},
41 {.name = LUA_BUFFERLIBNAME, .open = luaopen_buffer},
42 {.name = LUA_VECLIBNAME, .open = luaopen_vector},
46 constexpr const char*
HIDDEN_GLOBALS[] = {
"gcinfo",
"getfenv",
"setfenv",
"newproxy"};
49 const auto* entry =
static_cast<const LibraryEntry*
>(lua_tolightuserdata(state, 1));
50 lua_pushcfunction(state, entry->open, entry->name);
51 lua_pushstring(state, entry->name);
52 lua_call(state, 1, 0);
59 switch (lua_type(state, index)) {
60 case LUA_TNIL:
return "nil";
61 case LUA_TBOOLEAN:
return lua_toboolean(state, index) ?
"true" :
"false";
64 lua_pushvalue(state, index);
66 const char* text = lua_tolstring(state, -1, &length);
67 std::string result(text, length);
72 default:
return std::string(
"<") + luaL_typename(state, index) +
">";
79 std::expected<ScriptValue, std::string>
ReadValue(lua_State* state,
int index, std::vector<const void*>& path);
81 std::expected<ScriptValue, std::string>
ReadKey(lua_State* state,
int index) {
82 if (lua_type(state, index) == LUA_TSTRING) {
84 const char* text = lua_tolstring(state, index, &length);
89 if (lua_type(state, index) == LUA_TNUMBER)
92 return std::unexpected(
93 std::format(
"表の鍵にできるのは数と文字列だけ({} があった)", luaL_typename(state, index)));
96 std::expected<ScriptValue, std::string>
ReadTable(lua_State* state,
int index, std::vector<const void*>& path) {
97 const void* identity = lua_topointer(state, index);
100 if (std::ranges::find(path, identity) != path.end())
101 return std::unexpected(
"表が自分自身を含んでいる(循環)");
102 if (!lua_checkstack(state, 3))
103 return std::unexpected(
"Luau のスタックが足りない");
105 path.push_back(identity);
106 std::vector<ScriptField> fields;
110 while (lua_next(state, index) != 0) {
112 auto value = key ?
ReadValue(state, lua_gettop(state), path)
113 : std::expected<ScriptValue, std::string>{};
114 if (!key || !value) {
118 return std::unexpected(!key ? key.error() : value.error());
121 fields.push_back(
ScriptField{.key = std::move(*key), .value = std::move(*value)});
130 std::expected<ScriptValue, std::string>
ReadValue(lua_State* state,
int index, std::vector<const void*>& path) {
131 switch (lua_type(state, index)) {
134 case LUA_TSTRING:
return ReadKey(state, index);
135 case LUA_TTABLE:
return ReadTable(state, index, path);
137 const double number = lua_tonumber(state, index);
138 if (number != number)
139 return std::unexpected(
"NaN は表にできない(ビット列が機種で変わる)");
144 return std::unexpected(
145 std::format(
"{} は表にできない(数・文字列・真偽・表だけ)", luaL_typename(state, index)));
150 using Bytecode = std::unique_ptr<char,
decltype(&std::free)>;
153 lua_CompileOptions options{};
154 options.optimizationLevel = 1;
155 options.debugLevel = 1;
157 return Bytecode(luau_compile(source.data(), source.size(), &options, &bytecodeSize), &std::free);
161 if (safepointExceeded)
163 if (memoryExceeded || status == LUA_ERRMEM)
181 std::unique_ptr<LuauSandbox> sandbox(
new LuauSandbox(std::move(limits)));
184 if (!sandbox->m_state)
185 return std::unexpected(
"Luau の状態を作れない(メモリの上限が小さすぎる?)");
187 lua_Callbacks* callbacks = lua_callbacks(sandbox->m_state);
188 callbacks->userdata = sandbox.get();
191 if (
auto opened = sandbox->OpenLibraries(); !opened)
192 return std::unexpected(opened.error());
199 for (
const LibraryEntry& entry : ALLOWED_LIBRARIES) {
200 lua_pushcfunction(
m_state, &OpenOneLibrary,
"OpenOneLibrary");
201 lua_pushlightuserdata(
m_state,
const_cast<LibraryEntry*
>(&entry));
202 if (lua_pcall(
m_state, 1, 0, 0) != LUA_OK) {
203 std::string message = lua_tostring(
m_state, -1) ? lua_tostring(
m_state, -1) :
"?";
206 return std::unexpected(std::format(
"標準ライブラリ '{}' を開けない: {}", entry.name, message));
211 for (
const char* name : HIDDEN_GLOBALS) {
217 lua_setglobal(
m_state,
"print");
227 std::string message = std::format(
"Seal の後に {}.{} を登録しようとした", tableName, name);
233 lua_getglobal(
m_state, tableName);
234 if (!lua_istable(
m_state, -1)) {
238 lua_setglobal(
m_state, tableName);
242 lua_pushlightuserdata(
m_state, context);
243 lua_pushcclosure(
m_state, function, name, 1);
244 lua_setfield(
m_state, -2, name);
259 return lua_tolightuserdata(state, lua_upvalueindex(1));
266 lua_getglobal(
m_state, LUA_MATHLIBNAME);
267 lua_getfield(
m_state, -1,
"randomseed");
269 if (lua_pcall(
m_state, 1, 0, 0) != LUA_OK)
275 std::expected<RunResult, ScriptError>
LuauSandbox::Run(std::string_view chunkName, std::string_view source,
288 size_t bytecodeSize = 0;
289 const Bytecode bytecode =
Compile(source, bytecodeSize);
291 return std::unexpected(
295 lua_State* thread = lua_newthread(
m_state);
296 luaL_sandboxthread(thread);
299 auto result =
RunThread(thread, chunkName, bytecode.get(), bytecodeSize, options);
306 lua_gc(
m_state, LUA_GCCOLLECT, 0);
312 const char* bytecode,
size_t bytecodeSize,
314 const std::string luauChunkName =
"=" + std::string(chunkName);
315 if (luau_load(thread, luauChunkName.c_str(), bytecode, bytecodeSize, 0) != LUA_OK)
318 const int status = lua_pcall(thread, 0, LUA_MULTRET, 0);
319 if (status != LUA_OK) {
322 return std::unexpected(
ScriptError{.kind = kind, .message = ValueToText(thread, -1)});
333 const int returnCount = lua_gettop(thread);
334 for (
int index = 1; index <= returnCount; ++index) {
335 result.
returns.push_back(ValueToText(thread, index));
339 std::vector<const void*> path;
340 auto value = ReadValue(thread, index, path);
342 std::string message = std::format(
"{}: 戻り値 {}: {}", chunkName, index, value.error());
347 result.
values.push_back(std::move(*value));
367 lua_setglobal(thread,
"require");
380 const char* name = luaL_checkstring(state, 1);
381 auto* self =
static_cast<LuauSandbox*
>(lua_callbacks(state)->userdata);
386 auto loaded = self->LoadModule(state, name);
388 lua_pushlstring(state, loaded.error().data(), loaded.error().size());
401 return std::unexpected(
"require はパッケージの中だけで使える");
403 auto file = (*m_modules)(name);
405 return std::unexpected(std::format(
"require(\"{}\"): {}", name, file.error()));
409 lua_getfield(state, -1, file->chunkName.c_str());
410 if (!lua_isnil(state, -1)) {
411 lua_remove(state, -2);
421 chain += loading +
" → ";
423 return std::unexpected(std::format(
"require の循環: {}{}", chain, file->chunkName));
426 return RunModule(state, file->chunkName, file->source);
430 std::string_view source) {
432 size_t bytecodeSize = 0;
433 const Bytecode bytecode =
Compile(source, bytecodeSize);
437 return std::unexpected(
"コンパイルの結果を確保できない");
443 lua_pushvalue(state, LUA_GLOBALSINDEX);
444 lua_setfield(state, -2,
"__index");
445 lua_setreadonly(state, -1,
true);
446 lua_setmetatable(state, -2);
448 const std::string luauChunkName =
"=" + chunkName;
449 if (luau_load(state, luauChunkName.c_str(), bytecode.get(), bytecodeSize, lua_gettop(state)) != LUA_OK) {
450 std::string message = ValueToText(state, -1);
453 return std::unexpected(std::move(message));
456 lua_remove(state, -2);
460 const int status = lua_pcall(state, 0, 1, 0);
463 if (status != LUA_OK || lua_isnil(state, -1)) {
464 std::string message = status != LUA_OK ? ValueToText(state, -1)
465 : std::format(
"{}: モジュールは nil 以外の値を 1 つ返す", chunkName);
468 return std::unexpected(std::move(message));
471 lua_pushvalue(state, -1);
472 lua_setfield(state, -3, chunkName.c_str());
473 lua_remove(state, -2);
482 const size_t previousSize = pointer ? oldSize : 0;
486 self->m_memoryBytes -= previousSize;
492 const uint64_t nextBytes = self->m_memoryBytes - previousSize + newSize;
493 if (newSize > previousSize && nextBytes > self->m_limits.memoryLimitBytes) {
494 self->m_memoryExceeded =
true;
499 void* resized = std::realloc(pointer, newSize);
503 self->m_memoryBytes = nextBytes;
512 auto* self =
static_cast<LuauSandbox*
>(lua_callbacks(state)->userdata);
513 ++self->m_safepointCount;
514 if (self->m_safepointCount <= self->m_limits.safepointLimit)
518 self->m_safepointExceeded =
true;
519 const std::string message = std::format(
"安全点の上限({})を超えた", self->m_limits.safepointLimit);
520 luaL_error(state,
"%s", message.c_str());
524 auto* self =
static_cast<LuauSandbox*
>(lua_callbacks(state)->userdata);
527 const int argumentCount = lua_gettop(state);
528 for (
int index = 1; index <= argumentCount; ++index) {
531 text += ValueToText(state, index);
534 if (self->m_limits.printSink)
535 self->m_limits.printSink(self->m_currentChunk, text);
std::expected< RunResult, ScriptError > Run(std::string_view chunkName, std::string_view source, const RunOptions &options={})
void BeginModules(lua_State *thread, const ModuleResolver *modules)
std::string m_currentChunk
std::expected< void, ScriptError > RegisterFunction(const char *tableName, const char *name, HostFunction function, void *context)
std::expected< RunResult, ScriptError > CollectResults(lua_State *thread, std::string_view chunkName, const RunOptions &options)
static void Interrupt(lua_State *state, int gc)
std::expected< void, std::string > OpenLibraries()
uint64_t m_safepointCount
static int Require(lua_State *state)
std::expected< void, std::string > RunModule(lua_State *state, const std::string &chunkName, std::string_view source)
std::expected< void, std::string > LoadModule(lua_State *state, std::string_view name)
LuauSandbox(const LuauSandbox &)=delete
static std::expected< std::unique_ptr< LuauSandbox >, std::string > Create(SandboxLimits limits)
std::expected< RunResult, ScriptError > RunThread(lua_State *thread, std::string_view chunkName, const char *bytecode, size_t bytecodeSize, const RunOptions &options)
static void * Allocate(void *userData, void *pointer, size_t oldSize, size_t newSize)
const ModuleResolver * m_modules
std::vector< std::string > m_moduleLoading
static void * HostContext(lua_State *state)
static int Print(lua_State *state)
constexpr size_t MAX_VALUE_DEPTH
int OpenOneLibrary(lua_State *state)
std::expected< ScriptValue, std::string > ReadTable(lua_State *state, int index, std::vector< const void * > &path)
constexpr LibraryEntry ALLOWED_LIBRARIES[]
std::string ValueToText(lua_State *state, int index)
std::expected< ScriptValue, std::string > ReadKey(lua_State *state, int index)
constexpr const char * HIDDEN_GLOBALS[]
std::expected< ScriptValue, std::string > ReadValue(lua_State *state, int index, std::vector< const void * > &path)
ScriptErrorKind ClassifyError(int status, bool safepointExceeded, bool memoryExceeded)
std::unique_ptr< char, decltype(&std::free)> Bytecode
std::function< std::expected< ModuleFile, std::string >(std::string_view name)> ModuleResolver
int(*)(lua_State *state) HostFunction
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
const ModuleResolver * modules
std::vector< std::string > returns
std::vector< ScriptValue > values
static ScriptValue MakeString(std::string value)
static ScriptValue MakeTable(std::vector< ScriptField > fields)
static ScriptValue MakeNumber(double value)
static ScriptValue MakeBoolean(bool value)