1
0
mirror of https://github.com/odrling/Aegisub synced 2025-04-11 22:56:02 +02:00
arch1t3cht 0b40de8bea
C++ modernization and boost removal ()
* Bump boost to 1.83

* Switch to building as C++20

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>

* Fix many warnings

* Rip out boost::locale and just use ICU directly

* Fix

* Replace boost::filesystem with std::filesystem

* Fix

* Addditional c++ modernization

* Use string_view all over the place and rip out more boost stuff

* Simply BOM handling in charset conversions

* libaegisub: fix dispatch types

* Fix

* Bump subprojects

* Reorganize tests build file

* Fix remaining compilation errors on Linux

* Rip out the last bits of boost::filesystem

* Remove remaining uses of boost string joins

* Fix some errors introduced with refactors

* Revert "Simply BOM handling in charset conversions"

This reverts commit 2e6b26d5ffe69d7fb280bb1d18c6758112244b64.
Taking out the BOM handling broke tests, so it'll probably break more
stuff.

* Bring back IconvWrapper::RequiredBufferSize

This partially reverts 62befa996126659d139e9ad53e631780b6ed6122 .
This function is actually used in charset_conv_win.cpp

* Fix ifind after moving to ICU

The previous logic didn't check if the match was on parts of
decomposed characters, so it also failed the corresponding test.

* Remove incorrect karaoke_matcher test

This was clearly incorrect and probably just unfinished.

* Remove leftover boost::locale code

* Move iconv include to charset_conv.h

On newer mac sdks iconv_t is defined differently, so it's harder to
just have a typedef for it.

* Fix compilation on arm64 mac

wx uses a different string implementation here, and utf8_string()
doesn't exist there.

* Fix luajit dependency in luabins project

Since luajit always first tried using dependency(), further calls
of dependency() will also always return system luajit.
meson.override_dependency() won't work.
This makes luabins link system luajit where it's available while aegisub
itself uses the subproject's luajit, which causes all kinds of fun
issues and definitely didn't baffle me four hours...

The added solution for this is horribly ugly (and also has problems when
reconfiguring...) but it's the only one I found that works. Maybe it's
better to always require interal luajit after all, or make the user
choose with a meson option?

* Fix locale initialization

Previously this would fail on startup because the automation menu
uses boost::locale::comparator.
... Or maybe the locale init change should just be reverted entirely?
Or it should be something different? I don't really know.

* Revert "Fix luajit dependency in luabins project"

This reverts commit 340fb9c6125412d18f23c08c00f2bc8f46774b6d.

* Fix luajit dependency in luabins project, take 2

Thinking about it some more, just copying the detection logic is
probably the lesser evil here.

* Fix agi::split_iterator after refactor

is_end being removed caused it to not output an empty segment at the
end if the input ends with a delimiter, but existing usages relied
on it doing that.

* Fix style parsing after refactor

* Fix tons of implicit this captures

* Enable CI to test

* Update deprecated hunspell usage

* Fix tests compilation on mac

* Make sure wx subproject builds with c++14

* Fix compilation on Windows

* Revert "Bring back IconvWrapper::RequiredBufferSize"

This reverts commit 04f4b260a08de6497c583f025090ca4f9fe1ef6d.

* Pin libass wrap for now

Apparently dependency('iconv') breaks when iconv is overridden??

* Fix compilation with wx 3.0

* Fix startup crash on Windows

windows.h was defining the ERROR macro, which shadowed the
DialogueTokenType enum variant, which broke the lexer construction.

* Fix SplitText ICU logic

Include UBRK_WORD_IDEO and check the entire rules vec. This now matches
the logic of boost::locale.

* Add test for character_count with \N and friends

* Fix ass_dialogue parsing after refactor

* Revert "Pin libass wrap for now"

This reverts commit 3802bb7272a8bf4861c09b020c9eee9e643804fc.

* Remove iconv's stdbool.h

This was breaking things (libass) and doesn't seem to be
needed any more.

* Revert changes to to_wx

These broke some things, in particular FromUTF8Unchecked seems to not
like empty strings. Probably safer to just revert.

* Fix kara replacer after refactor

* Fix karaoke timing mode after refactor

* Revert "Enable CI to test"

This reverts commit 256cbebbfc9cf4eb5c497898670ac28ce24ce2fd.

---------

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
Co-authored-by: Thomas Goyne <plorkyeran@gmail.com>
2023-12-03 10:59:50 -08:00

264 lines
6.9 KiB
C++

// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "libaegisub/lua/utils.h"
#include "libaegisub/format.h"
#include "libaegisub/log.h"
#include "libaegisub/string.h"
#include <boost/range/adaptor/reversed.hpp>
#include <regex>
#ifdef _MSC_VER
// Disable warnings for noreturn functions having return types
#pragma warning(disable: 4645 4646)
#endif
namespace agi::lua {
std::string get_string_or_default(lua_State *L, int idx) {
size_t len = 0;
const char *str = lua_tolstring(L, idx, &len);
if (!str)
return "<not a string>";
return std::string(str, len);
}
std::string get_string(lua_State *L, int idx) {
size_t len = 0;
const char *str = lua_tolstring(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;
if (lua_isstring(L, -1))
ret = lua_tostring(L, -1);
lua_pop(L, 1);
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);
}
long 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
return lua_line;
}
push_value(L, file);
lua_rawget(L, -2);
if (!lua_istable(L, -1)) {
lua_pop(L, 2);
return lua_line;
}
lua_rawgeti(L, -1, lua_line);
if (!lua_isnumber(L, -1)) {
lua_pop(L, 3);
return lua_line;
}
auto char_pos = static_cast<size_t>(lua_tonumber(L, -1));
lua_pop(L, 3);
// The moonscript line tables give us a character offset into the file,
// so now we need to map that to a line number
lua_getfield(L, LUA_REGISTRYINDEX, ("raw moonscript: " + file).c_str());
if (!lua_isstring(L, -1)) {
lua_pop(L, 1);
return lua_line;
}
size_t moon_len;
auto moon = lua_tolstring(L, -1, &moon_len);
return std::count(moon, moon + std::min(moon_len, char_pos), '\n') + 1;
}
int add_stack_trace(lua_State *L) {
int level = 1;
if (lua_isnumber(L, 2)) {
level = (int)lua_tointeger(L, 2);
lua_pop(L, 1);
}
const char *err = lua_tostring(L, 1);
if (!err) return 1;
std::string message = err;
if (lua_gettop(L))
lua_pop(L, 1);
// Strip the location from the error message since it's redundant with
// the stack trace
std::regex location(R"(^\[string ".*"\]:[0-9]+: )");
message = regex_replace(message, location, "", std::regex_constants::format_first_only);
std::vector<std::string> frames;
frames.emplace_back(std::move(message));
lua_Debug ar;
while (lua_getstack(L, level++, &ar)) {
lua_getinfo(L, "Snl", &ar);
if (ar.what[0] == 't')
frames.emplace_back("(tail call)");
else {
bool is_moon = false;
std::string file = ar.source;
if (file == "=[C]")
file = "<C function>";
else if (file.ends_with(".moon"))
is_moon = true;
auto real_line = [&](int line) {
return is_moon ? moon_line(L, line, file) : line;
};
std::string function = ar.name ? ar.name : "";
if (*ar.what == 'm')
function = "<main>";
else if (*ar.what == 'C')
function = '?';
else if (!*ar.namewhat)
function = agi::format("<anonymous function at lines %d-%d>", real_line(ar.linedefined), real_line(ar.lastlinedefined - 1));
frames.emplace_back(agi::format(" File \"%s\", line %d\n%s", file, real_line(ar.currentline), function));
}
}
push_value(L, agi::Join("\n", frames | boost::adaptors::reversed));
return 1;
}
[[noreturn]] int 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();
}
[[noreturn]] int 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);
}
[[noreturn]] int 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);
}
int exception_wrapper(lua_State *L, int (*func)(lua_State *L)) {
try {
return func(L);
}
catch (agi::Exception const& e) {
push_value(L, e.GetMessage());
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();
}
}
#ifdef _DEBUG
void LuaStackcheck::check_stack(int additional) {
int top = lua_gettop(L);
if (top - additional != startstack) {
LOG_D("automation/lua") << "lua stack size mismatch.";
dump();
assert(top - additional == startstack);
}
}
void LuaStackcheck::dump() {
int top = lua_gettop(L);
LOG_D("automation/lua/stackdump") << "--- dumping lua stack...";
for (int i = top; i > 0; i--) {
lua_pushvalue(L, i);
std::string type(lua_typename(L, lua_type(L, -1)));
if (lua_isstring(L, i))
LOG_D("automation/lua/stackdump") << type << ": " << lua_tostring(L, -1);
else
LOG_D("automation/lua/stackdump") << type;
lua_pop(L, 1);
}
LOG_D("automation/lua") << "--- end dump";
}
#endif
}