Switch back to building Lua as C

In preparation for switching to LuaJIT, which doesn't support PUC Lua's
thing of using C++ exceptions for lua_error.

Requires replacing all uses of lua_error (and things calling lua_error)
with custom versions that throw an exception instead and adding an
exception -> lua error wrapper at all C++ -> Lua boundaries.
This commit is contained in:
Thomas Goyne 2014-04-27 07:20:11 -07:00
parent e3eb28ffd1
commit 9492192b73
64 changed files with 483 additions and 375 deletions

View file

@ -86,7 +86,6 @@ CFLAGS_ICU = @ICU_I18N_CFLAGS@
CFLAGS_LIBASS = @LIBASS_CFLAGS@
CFLAGS_LIBCURL = @LIBCURL_CFLAGS@
CFLAGS_LIBPULSE = @LIBPULSE_CFLAGS@
CFLAGS_LUA = -I../vendor/lua/src
CFLAGS_OPENAL = @OPENAL_CFLAGS@
CFLAGS_OSS = @OSS_CFLAGS@
CFLAGS_PORTAUDIO = @PORTAUDIO_CFLAGS@

View file

@ -4,7 +4,7 @@ PRECOMPILED_HEADER_NAME = ../libaegisub/lagi_pre.h
PROGRAM = aegisub-lua
CXXFLAGS += -I../libaegisub/include -I../src -I ../vendor/lua/src $(CXXFLAGS_WX)
CXXFLAGS += -I../libaegisub/include -I../src -I.. -I../vendor/lua/src $(CXXFLAGS_WX)
CPPFLAGS += $(CPPFLAGS_BOOST)
LIBS := -L../libaegisub -laegisub -L../vendor/lua -llua-aegisub $(LIBS)

View file

@ -20,9 +20,6 @@
#include <cstdio>
#include <cstdlib>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
using namespace agi::lua;

View file

@ -122,7 +122,10 @@
<ClCompile Include="$(SrcDir)common\vfr.cpp" />
<ClCompile Include="$(SrcDir)lua\modules.cpp" />
<ClCompile Include="$(SrcDir)lua\modules\lfs.cpp" />
<ClCompile Include="$(SrcDir)lua\modules\lpeg.cpp" />
<ClCompile Include="$(SrcDir)lua\modules\lpeg.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles></ForcedIncludeFiles>
</ClCompile>
<ClCompile Include="$(SrcDir)lua\modules\re.cpp" />
<ClCompile Include="$(SrcDir)lua\script_reader.cpp" />
<ClCompile Include="$(SrcDir)lua\utils.cpp" />

View file

@ -310,7 +310,7 @@
<ClCompile Include="$(SrcDir)lua\modules\lfs.cpp">
<Filter>Lua\Modules</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lua\modules\lpeg.cpp">
<ClCompile Include="$(SrcDir)lua\modules\lpeg.c">
<Filter>Lua\Modules</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lua\modules\re.cpp">

View file

@ -23,35 +23,35 @@
<!-- Source files -->
<ItemGroup>
<ClCompile Include="$(SrcDir)lapi.cpp" />
<ClCompile Include="$(SrcDir)lauxlib.cpp" />
<ClCompile Include="$(SrcDir)lbaselib.cpp" />
<ClCompile Include="$(SrcDir)lcode.cpp" />
<ClCompile Include="$(SrcDir)ldblib.cpp" />
<ClCompile Include="$(SrcDir)ldebug.cpp" />
<ClCompile Include="$(SrcDir)ldo.cpp" />
<ClCompile Include="$(SrcDir)ldump.cpp" />
<ClCompile Include="$(SrcDir)lfunc.cpp" />
<ClCompile Include="$(SrcDir)lgc.cpp" />
<ClCompile Include="$(SrcDir)linit.cpp" />
<ClCompile Include="$(SrcDir)liolib.cpp" />
<ClCompile Include="$(SrcDir)llex.cpp" />
<ClCompile Include="$(SrcDir)lmathlib.cpp" />
<ClCompile Include="$(SrcDir)lmem.cpp" />
<ClCompile Include="$(SrcDir)loadlib.cpp" />
<ClCompile Include="$(SrcDir)lobject.cpp" />
<ClCompile Include="$(SrcDir)lopcodes.cpp" />
<ClCompile Include="$(SrcDir)loslib.cpp" />
<ClCompile Include="$(SrcDir)lparser.cpp" />
<ClCompile Include="$(SrcDir)lstate.cpp" />
<ClCompile Include="$(SrcDir)lstring.cpp" />
<ClCompile Include="$(SrcDir)lstrlib.cpp" />
<ClCompile Include="$(SrcDir)ltable.cpp" />
<ClCompile Include="$(SrcDir)ltablib.cpp" />
<ClCompile Include="$(SrcDir)ltm.cpp" />
<ClCompile Include="$(SrcDir)lundump.cpp" />
<ClCompile Include="$(SrcDir)lvm.cpp" />
<ClCompile Include="$(SrcDir)lzio.cpp" />
<ClCompile Include="$(SrcDir)lapi.c" />
<ClCompile Include="$(SrcDir)lauxlib.c" />
<ClCompile Include="$(SrcDir)lbaselib.c" />
<ClCompile Include="$(SrcDir)lcode.c" />
<ClCompile Include="$(SrcDir)ldblib.c" />
<ClCompile Include="$(SrcDir)ldebug.c" />
<ClCompile Include="$(SrcDir)ldo.c" />
<ClCompile Include="$(SrcDir)ldump.c" />
<ClCompile Include="$(SrcDir)lfunc.c" />
<ClCompile Include="$(SrcDir)lgc.c" />
<ClCompile Include="$(SrcDir)linit.c" />
<ClCompile Include="$(SrcDir)liolib.c" />
<ClCompile Include="$(SrcDir)llex.c" />
<ClCompile Include="$(SrcDir)lmathlib.c" />
<ClCompile Include="$(SrcDir)lmem.c" />
<ClCompile Include="$(SrcDir)loadlib.c" />
<ClCompile Include="$(SrcDir)lobject.c" />
<ClCompile Include="$(SrcDir)lopcodes.c" />
<ClCompile Include="$(SrcDir)loslib.c" />
<ClCompile Include="$(SrcDir)lparser.c" />
<ClCompile Include="$(SrcDir)lstate.c" />
<ClCompile Include="$(SrcDir)lstring.c" />
<ClCompile Include="$(SrcDir)lstrlib.c" />
<ClCompile Include="$(SrcDir)ltable.c" />
<ClCompile Include="$(SrcDir)ltablib.c" />
<ClCompile Include="$(SrcDir)ltm.c" />
<ClCompile Include="$(SrcDir)lundump.c" />
<ClCompile Include="$(SrcDir)lvm.c" />
<ClCompile Include="$(SrcDir)lzio.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(SrcDir)lapi.h" />
@ -81,7 +81,7 @@
<Target Name="CopyHeaders" BeforeTargets="Build">
<Copy
SourceFiles="$(SrcDir)lua.h;$(SrcDir)lualib.h;$(SrcDir)lauxlib.h;$(SrcDir)luaconf.h"
SourceFiles="$(SrcDir)lua.h;$(SrcDir)lualib.h;$(SrcDir)lauxlib.h;$(SrcDir)luaconf.h;$(SrcDir)lua.hpp"
DestinationFolder="$(AegisubSourceBase)include"
SkipUnchangedFiles="true"
/>

View file

@ -15,91 +15,91 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)lapi.cpp">
<ClCompile Include="$(SrcDir)lapi.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lcode.cpp">
<ClCompile Include="$(SrcDir)lcode.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)ldo.cpp">
<ClCompile Include="$(SrcDir)ldo.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)ldump.cpp">
<ClCompile Include="$(SrcDir)ldump.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lfunc.cpp">
<ClCompile Include="$(SrcDir)lfunc.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lgc.cpp">
<ClCompile Include="$(SrcDir)lgc.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)linit.cpp">
<ClCompile Include="$(SrcDir)linit.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)llex.cpp">
<ClCompile Include="$(SrcDir)llex.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lmem.cpp">
<ClCompile Include="$(SrcDir)lmem.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lobject.cpp">
<ClCompile Include="$(SrcDir)lobject.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lopcodes.cpp">
<ClCompile Include="$(SrcDir)lopcodes.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lparser.cpp">
<ClCompile Include="$(SrcDir)lparser.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lstate.cpp">
<ClCompile Include="$(SrcDir)lstate.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lstring.cpp">
<ClCompile Include="$(SrcDir)lstring.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)ldebug.cpp">
<ClCompile Include="$(SrcDir)ldebug.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)ltable.cpp">
<ClCompile Include="$(SrcDir)ltable.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)ltm.cpp">
<ClCompile Include="$(SrcDir)ltm.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lundump.cpp">
<ClCompile Include="$(SrcDir)lundump.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lvm.cpp">
<ClCompile Include="$(SrcDir)lvm.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lzio.cpp">
<ClCompile Include="$(SrcDir)lzio.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lbaselib.cpp">
<ClCompile Include="$(SrcDir)lbaselib.c">
<Filter>Standard library</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)ltablib.cpp">
<ClCompile Include="$(SrcDir)ltablib.c">
<Filter>Standard library</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)ldblib.cpp">
<ClCompile Include="$(SrcDir)ldblib.c">
<Filter>Standard library</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)liolib.cpp">
<ClCompile Include="$(SrcDir)liolib.c">
<Filter>Standard library</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lmathlib.cpp">
<ClCompile Include="$(SrcDir)lmathlib.c">
<Filter>Standard library</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)loslib.cpp">
<ClCompile Include="$(SrcDir)loslib.c">
<Filter>Standard library</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lstrlib.cpp">
<ClCompile Include="$(SrcDir)lstrlib.c">
<Filter>Standard library</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)loadlib.cpp">
<ClCompile Include="$(SrcDir)loadlib.c">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)lauxlib.cpp">
<ClCompile Include="$(SrcDir)lauxlib.c">
<Filter>Standard library</Filter>
</ClCompile>
</ItemGroup>

View file

@ -17,20 +17,20 @@
<!-- Project specific configuration -->
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>LUABINS_LUABUILTASCPP;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SrcDir)..\..\lua51\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<!-- Source files -->
<ItemGroup>
<ClCompile Include="$(SrcDir)fwrite.cpp" />
<ClCompile Include="$(SrcDir)load.cpp" />
<ClCompile Include="$(SrcDir)luabins.cpp" />
<ClCompile Include="$(SrcDir)luainternals.cpp" />
<ClCompile Include="$(SrcDir)save.cpp" />
<ClCompile Include="$(SrcDir)savebuffer.cpp" />
<ClCompile Include="$(SrcDir)write.cpp" />
<ClCompile Include="$(SrcDir)fwrite.c" />
<ClCompile Include="$(SrcDir)load.c" />
<ClCompile Include="$(SrcDir)luabins.c" />
<ClCompile Include="$(SrcDir)luainternals.c" />
<ClCompile Include="$(SrcDir)save.c" />
<ClCompile Include="$(SrcDir)savebuffer.c" />
<ClCompile Include="$(SrcDir)write.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(SrcDir)fwrite.h" />

View file

@ -9,25 +9,25 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)fwrite.cpp">
<ClCompile Include="$(SrcDir)fwrite.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)load.cpp">
<ClCompile Include="$(SrcDir)load.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)luabins.cpp">
<ClCompile Include="$(SrcDir)luabins.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)luainternals.cpp">
<ClCompile Include="$(SrcDir)luainternals.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)save.cpp">
<ClCompile Include="$(SrcDir)save.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)savebuffer.cpp">
<ClCompile Include="$(SrcDir)savebuffer.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)write.cpp">
<ClCompile Include="$(SrcDir)write.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>

View file

@ -11,6 +11,7 @@ lagi_pre.h.gch: CXXFLAGS := $(CXXFLAGS)
common/charset_conv.o: CXXFLAGS += $(CFLAGS_ICONV)
common/parser.o: CXXFLAGS += -ftemplate-depth=256
unix/path.o: CXXFLAGS += -DP_DATA=\"$(P_DATA)\" -DP_DOC=\"$(P_DOC)\" -DP_LOCALE=\"$(P_LOCALE)\"
lua/**/*.o: CPPFLAGS += -I../vendor/lua/src
SRC += \
common/parser.cpp \
@ -43,7 +44,7 @@ SRC += \
common/vfr.cpp \
lua/modules.cpp \
lua/modules/lfs.cpp \
lua/modules/lpeg.cpp \
lua/modules/lpeg.c \
lua/modules/re.cpp \
lua/script_reader.cpp \
lua/utils.cpp \
@ -68,3 +69,5 @@ EXTRA_DIST += osx/util.mm
include ../Makefile.target
-include */*.d
-include lua/*/*.d
-include common/*/*.d

View file

@ -16,13 +16,22 @@
#include <libaegisub/fs.h>
#include <lua.h>
#include <lauxlib.h>
#include <boost/exception/detail/attribute_noreturn.hpp>
#include <lua.hpp>
#include <string>
#include <vector>
#include <type_traits>
namespace agi { namespace lua {
// Exception type for errors where the error details are on the lua stack
struct error_tag {};
// Below are functionally equivalent to the luaL_ functions, but using a C++
// exception for stack unwinding
int BOOST_ATTRIBUTE_NORETURN error(lua_State *L, const char *fmt, ...);
int BOOST_ATTRIBUTE_NORETURN argerror(lua_State *L, int narg, const char *extramsg);
int BOOST_ATTRIBUTE_NORETURN typerror(lua_State *L, int narg, const char *tname);
void argcheck(lua_State *L, bool cond, int narg, const char *msg);
inline void push_value(lua_State *L, bool value) { lua_pushboolean(L, value); }
inline void push_value(lua_State *L, const char *value) { lua_pushstring(L, value); }
@ -62,17 +71,51 @@ void push_value(lua_State *L, std::vector<T> const& value) {
}
}
/// Wrap a function which may throw exceptions and make it trigger lua errors
/// whenever it throws
template<int (*func)(lua_State *L)>
int exception_wrapper(lua_State *L) {
try {
return func(L);
}
catch (agi::Exception const& e) {
push_value(L, e.GetChainedMessage());
return lua_error(L);
}
catch (std::exception const& e) {
push_value(L, e.what());
return lua_error(L);
}
catch (error_tag) {
// Error message is already on the stack
return lua_error(L);
}
catch (...) {
std::terminate();
}
}
template<typename T>
void set_field(lua_State *L, const char *name, T value) {
push_value(L, value);
lua_setfield(L, -2, name);
}
template<int (*func)(lua_State *L)>
void set_field(lua_State *L, const char *name) {
push_value(L, exception_wrapper<func>);
lua_setfield(L, -2, name);
}
std::string get_string_or_default(lua_State *L, int idx);
std::string get_string(lua_State *L, int idx);
std::string check_string(lua_State *L, int idx);
std::string get_global_string(lua_State *L, const char *name);
std::string check_string(lua_State *L, int idx);
int check_int(lua_State *L, int idx);
size_t check_uint(lua_State *L, int idx);
void *check_udata(lua_State *L, int idx, const char *mt);
template<typename T, typename... Args>
T *make(lua_State *L, const char *mt, Args&&... args) {
auto obj = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
@ -84,7 +127,7 @@ T *make(lua_State *L, const char *mt, Args&&... args) {
template<typename T>
T& get(lua_State *L, int idx, const char *mt) {
return *static_cast<T *>(luaL_checkudata(L, idx, mt));
return *static_cast<T *>(check_udata(L, idx, mt));
}
struct LuaForEachBreak {};
@ -105,6 +148,8 @@ void lua_for_each(lua_State *L, Func&& func) {
lua_pop(L, 1); // pop table
}
/// Lua error handler which adds the stack trace to the error message, with
/// moonscript line rewriting support
int add_stack_trace(lua_State *L);
#ifdef _DEBUG

View file

@ -18,12 +18,10 @@
#include "libaegisub/lua/utils.h"
#include <lualib.h>
extern "C" int luaopen_luabins(lua_State *L);
extern "C" int luaopen_re_impl(lua_State *L);
int luaopen_lfs(lua_State *L);
int luaopen_lpeg(lua_State *L);
extern "C" int luaopen_lfs(lua_State *L);
extern "C" int luaopen_lpeg(lua_State *L);
namespace agi { namespace lua {
int regex_init(lua_State *L);

View file

@ -24,32 +24,30 @@ using namespace agi::lua;
using namespace agi::fs;
namespace bfs = boost::filesystem;
template<typename Func>
int call(lua_State *L, Func&& f) {
template<void (*func)(lua_State *L)>
int wrap(lua_State *L) {
try {
f();
func(L);
return 1;
}
catch (bfs::filesystem_error const& e) {
lua_pushnil(L);
lua_pushstring(L, e.what());
push_value(L, e.what());
return 2;
}
catch (agi::Exception const& e) {
lua_pushnil(L);
lua_pushstring(L, e.GetChainedMessage().c_str());
push_value(L, e.GetChainedMessage());
return 2;
}
}
template<void (*func)(lua_State *L)>
int wrap(lua_State *L) {
return call(L, [=] { func(L); });
catch (error_tag) {
return lua_error(L);
}
}
void chdir(lua_State *L) {
bfs::current_path(check_string(L, 1));
lua_pushboolean(L, true);
push_value(L, true);
}
void currentdir(lua_State *L) {
@ -58,17 +56,17 @@ void currentdir(lua_State *L) {
void mkdir(lua_State *L) {
CreateDirectory(check_string(L, 1));
lua_pushboolean(L, true);
push_value(L, true);
}
void rmdir(lua_State *L) {
Remove(check_string(L, 1));
lua_pushboolean(L, true);
push_value(L, true);
}
void touch(lua_State *L) {
Touch(check_string(L, 1));
lua_pushboolean(L, true);
push_value(L, true);
}
int dir_next(lua_State *L) {
@ -88,18 +86,13 @@ int dir_close(lua_State *L) {
}
int dir(lua_State *L) {
try {
const path p = check_string(L, 1);
push_value(L, dir_next);
make<agi::fs::DirectoryIterator>(L, "aegisub.lfs.dir", check_string(L, 1), "");
return 2;
}
catch (agi::Exception const& e) {
return luaL_error(L, "%s", e.GetChainedMessage().c_str());
}
const path p = check_string(L, 1);
push_value(L, dir_next);
make<agi::fs::DirectoryIterator>(L, "aegisub.lfs.dir", check_string(L, 1), "");
return 2;
}
int attributes(lua_State *L) {
void attributes(lua_State *L) {
static std::pair<const char *, void (*)(lua_State *, path const&)> fields[] = {
{"mode", [](lua_State *L, path const& p) {
switch (status(p).type()) {
@ -119,45 +112,43 @@ int attributes(lua_State *L) {
{"size", [](lua_State *L, path const& p) { push_value(L, Size(p)); }}
};
return call(L, [=] {
const path p = check_string(L, 1);
const path p = check_string(L, 1);
const auto field = get_string(L, 2);
if (!field.empty()) {
for (const auto getter : fields) {
if (field == getter.first) {
getter.second(L, p);
return;
}
}
luaL_error(L, "Invalid attribute name: %s", field.c_str());
}
lua_createtable(L, 0, boost::size(fields));
const auto field = get_string(L, 2);
if (!field.empty()) {
for (const auto getter : fields) {
getter.second(L, p);
lua_setfield(L, -2, getter.first);
if (field == getter.first) {
getter.second(L, p);
return;
}
}
});
error(L, "Invalid attribute name: %s", field.c_str());
}
lua_createtable(L, 0, boost::size(fields));
for (const auto getter : fields) {
getter.second(L, p);
lua_setfield(L, -2, getter.first);
}
}
}
int luaopen_lfs(lua_State *L) {
extern "C" int luaopen_lfs(lua_State *L) {
if (luaL_newmetatable(L, "aegisub.lfs.dir")) {
set_field(L, "__gc", dir_close);
set_field<dir_close>(L, "__gc");
lua_createtable(L, 0, 2);
set_field(L, "next", dir_next);
set_field(L, "close", dir_close);
set_field<dir_next>(L, "next");
set_field<dir_close>(L, "close");
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
const struct luaL_Reg lib[] = {
{"attributes", attributes},
{"attributes", wrap<attributes>},
{"chdir", wrap<chdir>},
{"currentdir", wrap<currentdir>},
{"dir", dir},
{"dir", exception_wrapper<dir>},
{"mkdir", wrap<mkdir>},
{"rmdir", wrap<rmdir>},
{"touch", wrap<touch>},

View file

@ -31,7 +31,7 @@ boost::smatch& get_smatch(lua_State *L) {
}
int regex_matches(lua_State *L) {
lua_pushboolean(L, u32regex_match(check_string(L, 2), get_regex(L)));
push_value(L, u32regex_match(check_string(L, 2), get_regex(L)));
return 1;
}
@ -52,9 +52,9 @@ int regex_match(lua_State *L) {
}
int regex_get_match(lua_State *L) {
auto match = get_smatch(L);
int idx = luaL_checkinteger(L, 2) - 1;
if (static_cast<size_t>(idx) > match.size() || !match[idx].matched) {
auto& match = get_smatch(L);
auto idx = check_uint(L, 2) - 1;
if (idx > match.size() || !match[idx].matched) {
lua_pushnil(L);
return 1;
}
@ -65,9 +65,10 @@ int regex_get_match(lua_State *L) {
}
int regex_search(lua_State *L) {
auto re = get_regex(L);
std::string str = check_string(L, 2);
int start = luaL_checkinteger(L, 3) - 1;
auto& re = get_regex(L);
auto str = check_string(L, 2);
auto start = check_uint(L, 3) - 1;
argcheck(L, start <= str.size(), 3, "out of bounds");
boost::smatch result;
if (!u32regex_search(str.cbegin() + start, str.cend(), result, re,
start > 0 ? boost::match_prev_avail | boost::match_not_bob : boost::match_default))
@ -82,10 +83,10 @@ int regex_search(lua_State *L) {
}
int regex_replace(lua_State *L) {
auto re = get_regex(L);
auto& re = get_regex(L);
const auto replacement = check_string(L, 2);
const std::string str = check_string(L, 3);
int max_count = luaL_checkinteger(L, 4);
const auto str = check_string(L, 3);
int max_count = check_int(L, 4);
// Can't just use regex_replace here since it can only do one or infinite replacements
auto match = boost::u32regex_iterator<std::string::const_iterator>(begin(str), end(str), re);
@ -110,8 +111,8 @@ int regex_replace(lua_State *L) {
}
int regex_compile(lua_State *L) {
std::string pattern(check_string(L, 1));
int flags = luaL_checkinteger(L, 2);
auto pattern(check_string(L, 1));
int flags = check_int(L, 2);
auto re = make<boost::u32regex>(L, "aegisub.regex");
try {
@ -175,23 +176,23 @@ int regex_init_flags(lua_State *L) {
extern "C" int luaopen_re_impl(lua_State *L) {
if (luaL_newmetatable(L, "aegisub.regex")) {
set_field(L, "__gc", regex_gc);
set_field<regex_gc>(L, "__gc");
lua_pop(L, 1);
}
if (luaL_newmetatable(L, "aegisub.smatch")) {
set_field(L, "__gc", smatch_gc);
set_field<smatch_gc>(L, "__gc");
lua_pop(L, 1);
}
lua_createtable(L, 0, 8);
set_field(L, "matches", regex_matches);
set_field(L, "search", regex_search);
set_field(L, "match", regex_match);
set_field(L, "get_match", regex_get_match);
set_field(L, "replace", regex_replace);
set_field(L, "compile", regex_compile);
set_field(L, "process_flags", regex_process_flags);
set_field(L, "init_flags", regex_init_flags);
set_field<regex_matches>(L, "matches");
set_field<regex_search>(L, "search");
set_field<regex_match>(L, "match");
set_field<regex_get_match>(L, "get_match");
set_field<regex_replace>(L, "replace");
set_field<regex_compile>(L, "compile");
set_field<regex_process_flags>(L, "process_flags");
set_field<regex_init_flags>(L, "init_flags");
return 1;
}

View file

@ -47,18 +47,19 @@ namespace agi { namespace lua {
if (!agi::fs::HasExtension(filename, "moon"))
return luaL_loadbuffer(L, buff, size, filename.string().c_str()) == 0;
// Save the text we'll be loading for the line number rewriting in the
// error handling
push_value(L, buff);
lua_setfield(L, LUA_REGISTRYINDEX, ("raw moonscript: " + filename.string()).c_str());
// We have a MoonScript file, so we need to load it with that
// It might be nice to have a dedicated lua state for compiling
// MoonScript to Lua
if (luaL_dostring(L, "return require('moonscript').loadstring"))
return false; // Leaves error message on stack
// Save the text we'll be loading for the line number rewriting in the
// error handling
lua_pushlstring(L, buff, size);
lua_pushstring(L, filename.string().c_str());
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, ("raw moonscript: " + filename.string()).c_str());
push_value(L, filename);
if (lua_pcall(L, 2, 2, 0))
return false; // Leaves error message on stack
@ -74,13 +75,13 @@ namespace agi { namespace lua {
static int module_loader(lua_State *L) {
int pretop = lua_gettop(L);
std::string module(luaL_checkstring(L, -1));
std::string module(check_string(L, -1));
boost::replace_all(module, ".", LUA_DIRSEP);
// Get the lua package include path (which the user may have modified)
lua_getglobal(L, "package");
lua_getfield(L, -1, "path");
std::string package_paths(luaL_checkstring(L, -1));
std::string package_paths(check_string(L, -1));
lua_pop(L, 2);
boost::char_separator<char> sep(";");
@ -99,7 +100,7 @@ namespace agi { namespace lua {
try {
if (!LoadFile(L, path))
return luaL_error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), luaL_checkstring(L, 1));
return error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), check_string(L, 1).c_str());
break;
}
catch (agi::fs::FileNotFound const&) {
@ -109,7 +110,7 @@ namespace agi { namespace lua {
// Not an error so swallow and continue on
}
catch (agi::Exception const& e) {
return luaL_error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), e.GetChainedMessage().c_str());
return error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), e.GetChainedMessage().c_str());
}
}
@ -138,7 +139,7 @@ namespace agi { namespace lua {
// Replace the default lua module loader with our unicode compatible one
lua_getfield(L, -1, "loaders");
push_value(L, module_loader);
push_value(L, exception_wrapper<module_loader>);
lua_rawseti(L, -2, 2);
lua_pop(L, 2);
}

View file

@ -24,6 +24,11 @@
#include <boost/range/adaptor/reversed.hpp>
#include <boost/regex.hpp>
#ifdef _MSC_VER
// Disable warnings for noreturn functions having return types
#pragma warning(disable: 4645 4646)
#endif
namespace agi { namespace lua {
std::string get_string_or_default(lua_State *L, int idx) {
size_t len = 0;
@ -39,12 +44,6 @@ std::string get_string(lua_State *L, int idx) {
return std::string(str ? str : "", len);
}
std::string check_string(lua_State *L, int idx) {
size_t len = 0;
const char *str = luaL_checklstring(L, idx, &len);
return std::string(str ? str : "", len);
}
std::string get_global_string(lua_State *L, const char *name) {
lua_getglobal(L, name);
std::string ret;
@ -54,6 +53,41 @@ std::string get_global_string(lua_State *L, const char *name) {
return ret;
}
std::string check_string(lua_State *L, int idx) {
size_t len = 0;
const char *str = lua_tolstring(L, idx, &len);
if (!str) typerror(L, idx, "string");
return std::string(str, len);
}
int check_int(lua_State *L, int idx) {
auto v = lua_tointeger(L, idx);
if (v == 0 && !lua_isnumber(L, idx))
typerror(L, idx, "number");
return v;
}
size_t check_uint(lua_State *L, int idx) {
auto v = lua_tointeger(L, idx);
if (v == 0 && !lua_isnumber(L, idx))
typerror(L, idx, "number");
if (v < 0)
argerror(L, idx, "must be >= 0");
return static_cast<size_t>(v);
}
void *check_udata(lua_State *L, int idx, const char *mt) {
void *p = lua_touserdata(L, idx);
if (!p) typerror(L, idx, mt);
if (!lua_getmetatable(L, idx)) typerror(L, idx, mt);
lua_getfield(L, LUA_REGISTRYINDEX, mt);
if (!lua_rawequal(L, -1, -2)) typerror(L, idx, mt);
lua_pop(L, 2);
return p;
}
static int moon_line(lua_State *L, int lua_line, std::string const& file) {
if (luaL_dostring(L, "return require 'moonscript.line_tables'")) {
lua_pop(L, 1); // pop error message
@ -147,6 +181,37 @@ int add_stack_trace(lua_State *L) {
return 1;
}
int BOOST_ATTRIBUTE_NORETURN error(lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
luaL_where(L, 1);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
throw error_tag();
}
int BOOST_ATTRIBUTE_NORETURN argerror(lua_State *L, int narg, const char *extramsg) {
lua_Debug ar;
if (!lua_getstack(L, 0, &ar))
error(L, "bad argument #%d (%s)", narg, extramsg);
lua_getinfo(L, "n", &ar);
if (strcmp(ar.namewhat, "method") == 0 && --narg == 0)
error(L, "calling '%s' on bad self (%s)", ar.name, extramsg);
if (!ar.name) ar.name = "?";
error(L, "bad argument #%d to '%s' (%s)",
narg, ar.name, extramsg);
}
int BOOST_ATTRIBUTE_NORETURN typerror(lua_State *L, int narg, const char *tname) {
const char *msg = lua_pushfstring(L, "%s expected, got %s",
tname, luaL_typename(L, narg));
argerror(L, narg, msg);
}
void argcheck(lua_State *L, bool cond, int narg, const char *msg) {
if (!cond) argerror(L, narg, msg);
}
#ifdef _DEBUG
void LuaStackcheck::check_stack(int additional) {

View file

@ -106,7 +106,7 @@ charset_detect.o: CXXFLAGS += -D_X86_
font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG)
text_file_reader.o: CXXFLAGS += -D_X86_
video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMS2)
auto4_lua.o auto4_lua_assfile.o auto4_lua_dialog.o auto4_lua_progresssink.o: CXXFLAGS += $(CFLAGS_LUA)
auto4_lua.o auto4_lua_assfile.o auto4_lua_dialog.o auto4_lua_progresssink.o: CPPFLAGS += -I../vendor/lua/src
SRC += \
MatroskaParser.c \

View file

@ -83,7 +83,7 @@ namespace {
wxString check_wxstring(lua_State *L, int idx)
{
return wxString::FromUTF8(luaL_checkstring(L, idx));
return to_wx(check_string(L, idx));
}
void set_context(lua_State *L, const agi::Context *c)
@ -134,7 +134,7 @@ namespace {
int clipboard_set(lua_State *L)
{
std::string str(luaL_checkstring(L, 1));
std::string str(check_string(L, 1));
bool succeeded = false;
@ -158,8 +158,8 @@ namespace {
int clipboard_init(lua_State *L)
{
lua_createtable(L, 0, 2);
set_field(L, "get", clipboard_get);
set_field(L, "set", clipboard_set);
set_field<clipboard_get>(L, "get");
set_field<clipboard_set>(L, "set");
return 1;
}
@ -189,7 +189,7 @@ namespace {
};
template<typename TableIter>
int pairs(lua_State *L) {
int pairs(lua_State *L) BOOST_NOEXCEPT {
// If the metamethod is defined, call it instead
if (luaL_getmetafield(L, 1, TableIter::method())) {
lua_pushvalue(L, 1);
@ -258,7 +258,7 @@ namespace {
int decode_path(lua_State *L)
{
std::string path = luaL_checkstring(L, 1);
std::string path = check_string(L, 1);
lua_pop(L, 1);
push_value(L, config::path->Decode(path));
return 1;
@ -267,13 +267,13 @@ namespace {
int cancel_script(lua_State *L)
{
lua_pushnil(L);
return lua_error(L);
throw error_tag();
}
int lua_text_textents(lua_State *L)
{
luaL_argcheck(L, lua_istable(L, 1), 1, "");
luaL_argcheck(L, lua_isstring(L, 2), 2, "");
argcheck(L, !!lua_istable(L, 1), 1, "");
argcheck(L, !!lua_isstring(L, 2), 2, "");
// have to check that it looks like a style table before actually converting
// if it's a dialogue table then an active AssFile object is required
@ -282,7 +282,7 @@ namespace {
std::string actual_class{lua_tostring(L, -1)};
boost::to_lower(actual_class);
if (actual_class != "style")
return luaL_error(L, "Not a style entry");
return error(L, "Not a style entry");
lua_pop(L, 1);
}
@ -291,11 +291,11 @@ namespace {
auto st = dynamic_cast<AssStyle*>(et.get());
lua_pop(L, 1);
if (!st)
return luaL_error(L, "Not a style entry");
return error(L, "Not a style entry");
double width, height, descent, extlead;
if (!Automation4::CalculateTextExtents(st, luaL_checkstring(L, 2), width, height, descent, extlead))
return luaL_error(L, "Some internal error occurred calculating text_extents");
if (!Automation4::CalculateTextExtents(st, check_string(L, 2), width, height, descent, extlead))
return error(L, "Some internal error occurred calculating text_extents");
push_value(L, width);
push_value(L, height);
@ -429,7 +429,7 @@ namespace {
lua_setglobal(L, "dofile");
lua_pushnil(L);
lua_setglobal(L, "loadfile");
push_value(L, LuaInclude);
push_value(L, exception_wrapper<LuaInclude>);
lua_setglobal(L, "include");
// replace pairs and ipairs with lua 5.2-style versions
@ -459,19 +459,19 @@ namespace {
lua_pushstring(L, "aegisub");
lua_createtable(L, 0, 12);
set_field(L, "register_macro", LuaCommand::LuaRegister);
set_field(L, "register_filter", LuaExportFilter::LuaRegister);
set_field(L, "text_extents", lua_text_textents);
set_field(L, "frame_from_ms", frame_from_ms);
set_field(L, "ms_from_frame", ms_from_frame);
set_field(L, "video_size", video_size);
set_field(L, "keyframes", get_keyframes);
set_field(L, "decode_path", decode_path);
set_field(L, "cancel", cancel_script);
set_field<LuaCommand::LuaRegister>(L, "register_macro");
set_field<LuaExportFilter::LuaRegister>(L, "register_filter");
set_field<lua_text_textents>(L, "text_extents");
set_field<frame_from_ms>(L, "frame_from_ms");
set_field<ms_from_frame>(L, "ms_from_frame");
set_field<video_size>(L, "video_size");
set_field<get_keyframes>(L, "keyframes");
set_field<decode_path>(L, "decode_path");
set_field<cancel_script>(L, "cancel");
set_field(L, "lua_automation_version", 4);
set_field(L, "__init_clipboard", clipboard_init);
set_field(L, "file_name", get_file_name);
set_field(L, "gettext", get_translation);
set_field<clipboard_init>(L, "__init_clipboard");
set_field<get_file_name>(L, "file_name");
set_field<get_translation>(L, "gettext");
// store aegisub table to globals
lua_settable(L, LUA_GLOBALSINDEX);
@ -550,8 +550,7 @@ namespace {
{
for (auto macro : macros) {
if (macro->name() == command->name()) {
luaL_error(L,
"A macro named '%s' is already defined in script '%s'",
error(L, "A macro named '%s' is already defined in script '%s'",
command->StrDisplay(nullptr).utf8_str().data(), name.c_str());
}
}
@ -581,7 +580,7 @@ namespace {
{
const LuaScript *s = GetScriptObject(L);
const std::string filename(luaL_checkstring(L, 1));
const std::string filename(check_string(L, 1));
agi::fs::path filepath;
// Relative or absolute path
@ -596,10 +595,10 @@ namespace {
}
if (!agi::fs::FileExists(filepath))
return luaL_error(L, "Lua include not found: %s", filename.c_str());
return error(L, "Lua include not found: %s", filename.c_str());
if (!LoadFile(L, filepath))
return luaL_error(L, "Error loading Lua include \"%s\":\n%s", filename.c_str(), luaL_checkstring(L, 1));
return error(L, "Error loading Lua include \"%s\":\n%s", filename.c_str(), check_string(L, 1).c_str());
int pretop = lua_gettop(L) - 1; // don't count the function value itself
lua_call(L, 0, LUA_MULTRET);
@ -677,10 +676,10 @@ namespace {
, cmd_type(cmd::COMMAND_NORMAL)
{
lua_getfield(L, LUA_REGISTRYINDEX, "filename");
cmd_name = str(boost::format("automation/lua/%s/%s") % luaL_checkstring(L, -1) % luaL_checkstring(L, 1));
cmd_name = str(boost::format("automation/lua/%s/%s") % check_string(L, -1) % check_string(L, 1));
if (!lua_isfunction(L, 3))
luaL_error(L, "The macro processing function must be a function");
error(L, "The macro processing function must be a function");
if (lua_isfunction(L, 4))
cmd_type |= cmd::COMMAND_VALIDATE;
@ -899,11 +898,11 @@ namespace {
// LuaFeatureFilter
LuaExportFilter::LuaExportFilter(lua_State *L)
: ExportFilter(luaL_checkstring(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3))
: ExportFilter(check_string(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3))
, LuaFeature(L)
{
if (!lua_isfunction(L, 4))
luaL_error(L, "The filter processing function must be a function");
error(L, "The filter processing function must be a function");
// new table for containing the functions for this feature
lua_createtable(L, 0, 2);

View file

@ -131,13 +131,13 @@ namespace Automation4 {
void LuaAssFile::CheckAllowModify()
{
if (!can_modify)
luaL_error(L, "Attempt to modify subtitles in read-only feature context.");
error(L, "Attempt to modify subtitles in read-only feature context.");
}
void LuaAssFile::CheckBounds(int idx)
{
if (idx <= 0 || idx > (int)lines.size())
luaL_error(L, "Requested out-of-range line from subtitle file: %d", idx);
error(L, "Requested out-of-range line from subtitle file: %d", idx);
}
void LuaAssFile::AssEntryToLua(lua_State *L, size_t idx)
@ -239,89 +239,82 @@ namespace Automation4 {
// convert it to a real AssEntry object, and pop the table from the stack
if (!lua_istable(L, -1))
luaL_error(L, "Can't convert a non-table value to AssEntry");
error(L, "Can't convert a non-table value to AssEntry");
lua_getfield(L, -1, "class");
if (!lua_isstring(L, -1))
luaL_error(L, "Table lacks 'class' field, can't convert to AssEntry");
error(L, "Table lacks 'class' field, can't convert to AssEntry");
std::string lclass(lua_tostring(L, -1));
boost::to_lower(lclass);
lua_pop(L, 1);
std::unique_ptr<AssEntry> result;
try {
if (lclass == "info")
result = agi::make_unique<AssInfo>(get_string_field(L, "key", "info"), get_string_field(L, "value", "info"));
else if (lclass == "style") {
auto sty = new AssStyle;
result.reset(sty);
sty->name = get_string_field(L, "name", "style");
sty->font = get_string_field(L, "fontname", "style");
sty->fontsize = get_double_field(L, "fontsize", "style");
sty->primary = get_string_field(L, "color1", "style");
sty->secondary = get_string_field(L, "color2", "style");
sty->outline = get_string_field(L, "color3", "style");
sty->shadow = get_string_field(L, "color4", "style");
sty->bold = get_bool_field(L, "bold", "style");
sty->italic = get_bool_field(L, "italic", "style");
sty->underline = get_bool_field(L, "underline", "style");
sty->strikeout = get_bool_field(L, "strikeout", "style");
sty->scalex = get_double_field(L, "scale_x", "style");
sty->scaley = get_double_field(L, "scale_y", "style");
sty->spacing = get_double_field(L, "spacing", "style");
sty->angle = get_double_field(L, "angle", "style");
sty->borderstyle = get_int_field(L, "borderstyle", "style");
sty->outline_w = get_double_field(L, "outline", "style");
sty->shadow_w = get_double_field(L, "shadow", "style");
sty->alignment = get_int_field(L, "align", "style");
sty->Margin[0] = get_int_field(L, "margin_l", "style");
sty->Margin[1] = get_int_field(L, "margin_r", "style");
sty->Margin[2] = get_int_field(L, "margin_t", "style");
sty->encoding = get_int_field(L, "encoding", "style");
sty->UpdateData();
}
else if (lclass == "dialogue") {
assert(ass != 0); // since we need AssFile::AddExtradata
auto dia = new AssDialogue;
result.reset(dia);
dia->Comment = get_bool_field(L, "comment", "dialogue");
dia->Layer = get_int_field(L, "layer", "dialogue");
dia->Start = get_int_field(L, "start_time", "dialogue");
dia->End = get_int_field(L, "end_time", "dialogue");
dia->Style = get_string_field(L, "style", "dialogue");
dia->Actor = get_string_field(L, "actor", "dialogue");
dia->Margin[0] = get_int_field(L, "margin_l", "dialogue");
dia->Margin[1] = get_int_field(L, "margin_r", "dialogue");
dia->Margin[2] = get_int_field(L, "margin_t", "dialogue");
dia->Effect = get_string_field(L, "effect", "dialogue");
dia->Text = get_string_field(L, "text", "dialogue");
lua_getfield(L, -1, "extra");
std::vector<uint32_t> new_ids;
lua_pushnil(L);
while (lua_next(L, -2)) {
auto key = get_string_or_default(L, -2);
auto value = get_string_or_default(L, -1);
new_ids.push_back(ass->AddExtradata(key, value));
lua_pop(L, 1);
}
lua_pop(L, 1);
dia->ExtradataIds = new_ids;
}
else {
luaL_error(L, "Found line with unknown class: %s", lclass.c_str());
return nullptr;
}
return result;
if (lclass == "info")
result = agi::make_unique<AssInfo>(get_string_field(L, "key", "info"), get_string_field(L, "value", "info"));
else if (lclass == "style") {
auto sty = new AssStyle;
result.reset(sty);
sty->name = get_string_field(L, "name", "style");
sty->font = get_string_field(L, "fontname", "style");
sty->fontsize = get_double_field(L, "fontsize", "style");
sty->primary = get_string_field(L, "color1", "style");
sty->secondary = get_string_field(L, "color2", "style");
sty->outline = get_string_field(L, "color3", "style");
sty->shadow = get_string_field(L, "color4", "style");
sty->bold = get_bool_field(L, "bold", "style");
sty->italic = get_bool_field(L, "italic", "style");
sty->underline = get_bool_field(L, "underline", "style");
sty->strikeout = get_bool_field(L, "strikeout", "style");
sty->scalex = get_double_field(L, "scale_x", "style");
sty->scaley = get_double_field(L, "scale_y", "style");
sty->spacing = get_double_field(L, "spacing", "style");
sty->angle = get_double_field(L, "angle", "style");
sty->borderstyle = get_int_field(L, "borderstyle", "style");
sty->outline_w = get_double_field(L, "outline", "style");
sty->shadow_w = get_double_field(L, "shadow",