diff --git a/src/luastate.cc b/src/luastate.cc index a94b57c..ac33c52 100644 --- a/src/luastate.cc +++ b/src/luastate.cc @@ -84,6 +84,18 @@ bool LuaState::EnsureOpen(Napi::Env env) { return true; } +// Indexing a non-table raises an unprotected Lua error, which aborts the whole +// process rather than throwing. Reject it here instead. +bool LuaState::EnsureIndexable(Napi::Env env, int index, const char* method) { + if(!lua_istable(lua_, index) && lua_type(lua_, index) != LUA_TUSERDATA){ + std::string message = std::string("LuaState.") + method + + ": Value At The Given Index Is Not A Table"; + Napi::TypeError::New(env, message).ThrowAsJavaScriptException(); + return false; + } + return true; +} + int LuaState::CallFunction(lua_State* L){ const char* func_name = lua_tostring(L, lua_upvalueindex(1)); LuaState* self = static_cast(lua_touserdata(L, lua_upvalueindex(2))); @@ -124,23 +136,7 @@ int LuaState::CallFunction(lua_State* L){ Napi::Value LuaState::RegisterFunction(const Napi::CallbackInfo& info){ Napi::Env env = info.Env(); - - if(info.Length() < 2){ - Napi::TypeError::New(env, "LuaState.RegisterFunction Requires 2 Arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.RegisterFunction Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[1].IsFunction()){ - Napi::TypeError::New(env, "LuaState.RegisterFunction Argument 2 Must Be A Function").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "RegisterFunction", {Arg::String, Arg::Function}) || !EnsureOpen(env)){ return env.Undefined(); } @@ -160,18 +156,7 @@ Napi::Value LuaState::RegisterFunction(const Napi::CallbackInfo& info){ Napi::Value LuaState::AddPackagePath(const Napi::CallbackInfo& info){ Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.AddPackagePath Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.AddPackagePath Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "AddPackagePath", {Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } @@ -208,28 +193,13 @@ Napi::Value LuaState::AddPackagePath(const Napi::CallbackInfo& info){ Napi::Value LuaState::LoadFile(const Napi::CallbackInfo& info){ Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.LoadFile Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.LoadFile Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "LoadFile", {Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } std::string file_name = info[0].As().Utf8Value(); - if(luaL_loadfile(lua_, file_name.c_str())){ - std::string message = lua_error_message(lua_, "LuaState.LoadFile: Parsing Of File " + file_name + " Has Failed:\n"); - lua_pop(lua_, 1); - Napi::Error::New(env, message).ThrowAsJavaScriptException(); - return env.Undefined(); + ThrowLuaError(env, lua_, "LuaState.LoadFile: Parsing Of File " + file_name + " Has Failed:\n"); } return env.Undefined(); @@ -237,28 +207,13 @@ Napi::Value LuaState::LoadFile(const Napi::CallbackInfo& info){ Napi::Value LuaState::LoadString(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.LoadString Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.LoadString Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "LoadString", {Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } std::string lua_code = info[0].As().Utf8Value(); - if(luaL_loadstring(lua_, lua_code.c_str())){ - std::string message = lua_error_message(lua_, "LuaState.LoadString: Parsing Of Lua Code Has Failed:\n"); - lua_pop(lua_, 1); - Napi::Error::New(env, message).ThrowAsJavaScriptException(); - return env.Undefined(); + ThrowLuaError(env, lua_, "LuaState.LoadString: Parsing Of Lua Code Has Failed:\n"); } return env.Undefined(); @@ -266,28 +221,13 @@ Napi::Value LuaState::LoadString(const Napi::CallbackInfo& info) { Napi::Value LuaState::DoFile(const Napi::CallbackInfo& info){ Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.DoFile Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.DoFile Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "DoFile", {Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } std::string file_name = info[0].As().Utf8Value(); - if(luaL_dofile(lua_, file_name.c_str())){ - std::string message = lua_error_message(lua_, "LuaState.DoFile: Execution Of File " + file_name + " Has Failed:\n"); - lua_pop(lua_, 1); - Napi::Error::New(env, message).ThrowAsJavaScriptException(); - return env.Undefined(); + ThrowLuaError(env, lua_, "LuaState.DoFile: Execution Of File " + file_name + " Has Failed:\n"); } return env.Undefined(); @@ -295,28 +235,13 @@ Napi::Value LuaState::DoFile(const Napi::CallbackInfo& info){ Napi::Value LuaState::DoString(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.DoString Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.DoString Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "DoString", {Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } std::string lua_code = info[0].As().Utf8Value(); - if(luaL_dostring(lua_, lua_code.c_str())){ - std::string message = lua_error_message(lua_, "LuaState.DoString: Execution Of Lua Code Has Failed:\n"); - lua_pop(lua_, 1); - Napi::Error::New(env, message).ThrowAsJavaScriptException(); - return env.Undefined(); + ThrowLuaError(env, lua_, "LuaState.DoString: Execution Of Lua Code Has Failed:\n"); } return env.Undefined(); @@ -324,18 +249,7 @@ Napi::Value LuaState::DoString(const Napi::CallbackInfo& info) { Napi::Value LuaState::SetGlobal(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.SetGlobal Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.SetGlobal Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "SetGlobal", {Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } @@ -347,18 +261,7 @@ Napi::Value LuaState::SetGlobal(const Napi::CallbackInfo& info) { Napi::Value LuaState::GetGlobal(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.GetGlobal Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsString()){ - Napi::TypeError::New(env, "LuaState.GetGlobal Argument 1 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "GetGlobal", {Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } @@ -370,38 +273,19 @@ Napi::Value LuaState::GetGlobal(const Napi::CallbackInfo& info) { Napi::Value LuaState::SetField(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 3){ - Napi::TypeError::New(env, "LuaState.SetField Requires 3 Arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.SetField Argument 1 Must Be A Number").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[1].IsString()){ - Napi::TypeError::New(env, "LuaState.SetField Argument 2 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "SetField", {Arg::Number, Arg::String, Arg::Any}) || !EnsureOpen(env)){ return env.Undefined(); } // Resolve the index before pushing: pushing the value shifts every relative // index by one, which would otherwise leave us assigning into the value itself. int index = abs_index(lua_, info[0].As().Int32Value()); - std::string field_name = info[1].As().Utf8Value(); - - // Indexing a non-table raises an unprotected Lua error, which aborts the whole - // process rather than throwing. Reject it here instead. - if(!lua_istable(lua_, index) && lua_type(lua_, index) != LUA_TUSERDATA){ - Napi::TypeError::New(env, "LuaState.SetField: Value At The Given Index Is Not A Table").ThrowAsJavaScriptException(); + if(!EnsureIndexable(env, index, "SetField")){ return env.Undefined(); } + std::string field_name = info[1].As().Utf8Value(); + // Push the value, not the key: lua_setfield takes the key as a C string and // pops the value from the top of the stack. push_value_to_lua(lua_, info[2]); @@ -412,36 +296,16 @@ Napi::Value LuaState::SetField(const Napi::CallbackInfo& info) { Napi::Value LuaState::GetField(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 2){ - Napi::TypeError::New(env, "LuaState.GetField Requires 2 Arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.GetField Argument 1 Must Be A Number").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[1].IsString()){ - Napi::TypeError::New(env, "LuaState.GetField Argument 2 Must Be A String").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "GetField", {Arg::Number, Arg::String}) || !EnsureOpen(env)){ return env.Undefined(); } int index = abs_index(lua_, info[0].As().Int32Value()); - std::string field_name = info[1].As().Utf8Value(); - - // Indexing a non-table raises an unprotected Lua error, which aborts the whole - // process rather than throwing. Reject it here instead. - if(!lua_istable(lua_, index) && lua_type(lua_, index) != LUA_TUSERDATA){ - Napi::TypeError::New(env, "LuaState.GetField: Value At The Given Index Is Not A Table").ThrowAsJavaScriptException(); + if(!EnsureIndexable(env, index, "GetField")){ return env.Undefined(); } + std::string field_name = info[1].As().Utf8Value(); lua_getfield(lua_, index, field_name.c_str()); return env.Undefined(); @@ -449,44 +313,16 @@ Napi::Value LuaState::GetField(const Napi::CallbackInfo& info) { Napi::Value LuaState::ToValue(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.ToValue Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.ToValue Argument 1 Must Be A Number").ThrowAsJavaScriptException(); + if(!CheckArgs(info, "ToValue", {Arg::Number}) || !EnsureOpen(env)){ return env.Undefined(); } - if(!EnsureOpen(env)){ - return env.Undefined(); - } - - int index = info[0].As().Int32Value(); - return lua_to_value(env, lua_, index); + return lua_to_value(env, lua_, info[0].As().Int32Value()); } Napi::Value LuaState::Call(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 2){ - Napi::TypeError::New(env, "LuaState.Call Requires 2 Arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.Call Argument 1 Must Be A Number").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[1].IsNumber()){ - Napi::TypeError::New(env, "LuaState.Call Argument 2 Must Be A Number").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "Call", {Arg::Number, Arg::Number}) || !EnsureOpen(env)){ return env.Undefined(); } @@ -494,10 +330,7 @@ Napi::Value LuaState::Call(const Napi::CallbackInfo& info) { int results = info[1].As().Int32Value(); if(lua_pcall(lua_, args, results, 0)){ - std::string message = lua_error_message(lua_, "LuaState.Call: Execution Of Lua Function Has Failed:\n"); - lua_pop(lua_, 1); - Napi::Error::New(env, message).ThrowAsJavaScriptException(); - return env.Undefined(); + ThrowLuaError(env, lua_, "LuaState.Call: Execution Of Lua Function Has Failed:\n"); } return env.Undefined(); @@ -505,47 +338,21 @@ Napi::Value LuaState::Call(const Napi::CallbackInfo& info) { Napi::Value LuaState::Yield(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.Yield Requires 1 Argument").ThrowAsJavaScriptException(); + if(!CheckArgs(info, "Yield", {Arg::Number}) || !EnsureOpen(env)){ return env.Undefined(); } - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.Yield Argument 1 Must Be A Number").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ - return env.Undefined(); - } - - int args = info[0].As().Int32Value(); - lua_yield(lua_, args); - + lua_yield(lua_, info[0].As().Int32Value()); return env.Undefined(); } Napi::Value LuaState::Resume(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.Resume Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.Resume Argument 1 Must Be A Number").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "Resume", {Arg::Number}) || !EnsureOpen(env)){ return env.Undefined(); } - int args = info[0].As().Int32Value(); - int status = lua_resume(lua_, args); - + int status = lua_resume(lua_, info[0].As().Int32Value()); return Napi::Number::New(env, status); } @@ -561,7 +368,6 @@ Napi::Value LuaState::Close(const Napi::CallbackInfo& info) { Napi::Value LuaState::Status(const Napi::CallbackInfo& info){ Napi::Env env = info.Env(); - if(!EnsureOpen(env)){ return env.Undefined(); } @@ -571,18 +377,7 @@ Napi::Value LuaState::Status(const Napi::CallbackInfo& info){ Napi::Value LuaState::CollectGarbage(const Napi::CallbackInfo& info){ Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.CollectGarbage Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.CollectGarbage Argument 1 Must Be A Number, try nodelua.GC.[TYPE]").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "CollectGarbage", {Arg::Number}, ", try nodelua.GC.[TYPE]") || !EnsureOpen(env)){ return env.Undefined(); } @@ -592,13 +387,7 @@ Napi::Value LuaState::CollectGarbage(const Napi::CallbackInfo& info){ Napi::Value LuaState::Push(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.Push Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!EnsureOpen(env)){ + if(!CheckArgs(info, "Push", {Arg::Any}) || !EnsureOpen(env)){ return env.Undefined(); } @@ -608,11 +397,11 @@ Napi::Value LuaState::Push(const Napi::CallbackInfo& info) { Napi::Value LuaState::Pop(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - if(!EnsureOpen(env)){ return env.Undefined(); } + // The count is optional and defaults to 1, so this cannot use CheckArgs. int pop_n = 1; if(info.Length() > 0 && info[0].IsNumber()){ pop_n = info[0].As().Int32Value(); @@ -624,7 +413,6 @@ Napi::Value LuaState::Pop(const Napi::CallbackInfo& info) { Napi::Value LuaState::GetTop(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - if(!EnsureOpen(env)){ return env.Undefined(); } @@ -634,11 +422,11 @@ Napi::Value LuaState::GetTop(const Napi::CallbackInfo& info) { Napi::Value LuaState::SetTop(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - if(!EnsureOpen(env)){ return env.Undefined(); } + // The index is optional and defaults to 0, so this cannot use CheckArgs. int set_n = 0; if(info.Length() > 0 && info[0].IsNumber()){ set_n = info[0].As().Int32Value(); @@ -650,23 +438,10 @@ Napi::Value LuaState::SetTop(const Napi::CallbackInfo& info) { Napi::Value LuaState::Replace(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - - if(info.Length() < 1){ - Napi::TypeError::New(env, "LuaState.Replace Requires 1 Argument").ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if(!info[0].IsNumber()){ - Napi::TypeError::New(env, "LuaState.Replace Argument 1 Must Be A Number").ThrowAsJavaScriptException(); + if(!CheckArgs(info, "Replace", {Arg::Number}) || !EnsureOpen(env)){ return env.Undefined(); } - if(!EnsureOpen(env)){ - return env.Undefined(); - } - - int index = info[0].As().Int32Value(); - lua_replace(lua_, index); - + lua_replace(lua_, info[0].As().Int32Value()); return env.Undefined(); } diff --git a/src/luastate.h b/src/luastate.h index 0d08e91..1b64ea1 100644 --- a/src/luastate.h +++ b/src/luastate.h @@ -35,6 +35,10 @@ class LuaState : public Napi::ObjectWrap { // calling into a closed LuaState raises instead of dereferencing freed memory. bool EnsureOpen(Napi::Env env); + // Throws and returns false if the value at `index` cannot be indexed, which + // would otherwise raise an unprotected Lua error and abort the process. + bool EnsureIndexable(Napi::Env env, int index, const char* method); + Napi::Value Close(const Napi::CallbackInfo& info); Napi::Value CollectGarbage(const Napi::CallbackInfo& info); diff --git a/src/utils.cc b/src/utils.cc index c69f78a..d3bd225 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,5 +1,63 @@ #include "utils.h" +namespace { + +const char* ArgTypeName(Arg kind){ + switch(kind){ + case Arg::Number: return "A Number"; + case Arg::String: return "A String"; + case Arg::Function: return "A Function"; + default: return "A Value"; + } +} + +bool ArgMatches(Napi::Value value, Arg kind){ + switch(kind){ + case Arg::Number: return value.IsNumber(); + case Arg::String: return value.IsString(); + case Arg::Function: return value.IsFunction(); + default: return true; + } +} + +} // namespace + +bool CheckArgs(const Napi::CallbackInfo& info, const char* method, + std::initializer_list expected, const char* hint){ + Napi::Env env = info.Env(); + const size_t required = expected.size(); + + if(info.Length() < required){ + std::string message = std::string("LuaState.") + method + " Requires " + + std::to_string(required) + (required == 1 ? " Argument" : " Arguments"); + Napi::TypeError::New(env, message).ThrowAsJavaScriptException(); + return false; + } + + size_t i = 0; + for(Arg kind : expected){ + if(!ArgMatches(info[i], kind)){ + std::string message = std::string("LuaState.") + method + " Argument " + + std::to_string(i + 1) + " Must Be " + ArgTypeName(kind); + if(hint != NULL){ + message += hint; + } + Napi::TypeError::New(env, message).ThrowAsJavaScriptException(); + return false; + } + ++i; + } + + return true; +} + +void ThrowLuaError(Napi::Env env, lua_State* L, const std::string& prefix){ + const char* message = lua_tostring(L, -1); + std::string full = prefix + (message ? message : "unknown error"); + lua_pop(L, 1); + Napi::Error::New(env, full).ThrowAsJavaScriptException(); +} + int abs_index(lua_State* L, int index) { if(index > 0 || index <= LUA_REGISTRYINDEX){ return index; @@ -63,8 +121,3 @@ void push_value_to_lua(lua_State* L, Napi::Value value){ lua_pushnil(L); } } - -std::string lua_error_message(lua_State* L, const std::string& prefix){ - const char* message = lua_tostring(L, -1); - return prefix + (message ? message : "unknown error"); -} diff --git a/src/utils.h b/src/utils.h index 9fce934..f58f846 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,6 +1,7 @@ #ifndef UTILS_H #define UTILS_H +#include #include #include @@ -9,6 +10,24 @@ extern "C" { #include } +// Kinds of JavaScript argument the binding methods accept. `Any` only asserts that +// the argument was supplied. +enum class Arg { Number, String, Function, Any }; + +// Checks arity and argument types for a LuaState method, throwing a TypeError in +// this project's established wording if either is wrong. `hint` is appended to a +// type error when a method wants to point at the constants to use. +// +// Returns false when it threw, so callers read as: +// if(!CheckArgs(info, "SetField", {Arg::Number, Arg::String, Arg::Any})) return env.Undefined(); +bool CheckArgs(const Napi::CallbackInfo& info, const char* method, + std::initializer_list expected, const char* hint = NULL); + +// Throws the Lua error on top of the stack as a JavaScript Error prefixed with +// `prefix`, and pops it. Popping here rather than at each call site is the point: +// leaving the error behind silently grows the stack. +void ThrowLuaError(Napi::Env env, lua_State* L, const std::string& prefix); + // Resolves a relative stack index to an absolute one, leaving positive indices // and pseudo-indices (LUA_GLOBALSINDEX and friends) untouched. Needed wherever we // push onto the stack before consuming a caller-supplied index. @@ -22,7 +41,4 @@ Napi::Value lua_to_value(Napi::Env env, lua_State* L, int index); // are pushed as nil. void push_value_to_lua(lua_State* L, Napi::Value value); -// Builds "" from the error at the top of the stack. -std::string lua_error_message(lua_State* L, const std::string& prefix); - #endif