Skip to content

Commit 643e6a2

Browse files
committed
Fix the testing version of ll.GetSubString() to be moderately sane
1 parent d3f09bc commit 643e6a2

File tree

5 files changed

+84
-48
lines changed

5 files changed

+84
-48
lines changed

VM/include/llsl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ LUA_API const float *luaSL_checkquaternion(lua_State *L, int num_arg);
8080
LUA_API int luaSL_pushnativeinteger(lua_State *L, int val);
8181
LUA_API void luaSL_pushindexlike(lua_State *L, int index);
8282
LUA_API int luaSL_checkindexlike(lua_State *L, int index);
83+
LUA_API int luaSL_checkobjectindex(lua_State *L, int len, int idx, bool compat_mode);
8384
LUA_API void luaSL_pushboollike(lua_State *L, int val);
8485
LUA_API uint8_t luaSL_lsl_type(lua_State *L, int idx);
8586
/// Should only be called in an interrupt handler!

VM/src/lll.cpp

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,36 +32,13 @@ static int ll_getlistlength(lua_State *L)
3232
return 1;
3333
}
3434

35-
static int _to_positive_index(lua_State *L, int len, int idx, bool compat_mode)
36-
{
37-
if (!compat_mode)
38-
{
39-
if (idx == 0)
40-
{
41-
luaL_error(L, "passed 0 when a 1-based index was expected");
42-
}
43-
else if (idx > 0)
44-
{
45-
// positive indices need to be shifted down to be 0-based.
46-
// negative indices are handled later.
47-
idx -= 1;
48-
}
49-
}
50-
51-
if(idx < 0)
52-
{
53-
return len + idx;
54-
}
55-
return idx;
56-
}
57-
5835
static int _list_accessor_helper(lua_State *L, LSLIType type)
5936
{
6037
luaL_checktype(L, 1, LUA_TTABLE);
6138
auto *h = hvalue(luaA_toobject(L, 1));
6239
int len = luaH_getn(h);
6340
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
64-
int idx = _to_positive_index(L, len, luaL_checkinteger(L, 2), compat_mode);
41+
int idx = luaSL_checkobjectindex(L, len, 2, compat_mode);
6542
if (idx < len && idx >= 0)
6643
{
6744
lua_pushcfunction(L, lsl_cast_list_elem, "lsl_cast_list_elem");
@@ -121,7 +98,7 @@ static int ll_list2vector(lua_State *L)
12198

12299
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
123100

124-
int idx = _to_positive_index(L, len, luaL_checkinteger(L, 2), compat_mode);
101+
int idx = luaSL_checkobjectindex(L, len, 2, compat_mode);
125102
if (idx < len && idx >= 0)
126103
{
127104
// This accessor does NOT auto-cast!
@@ -144,7 +121,7 @@ static int ll_list2rot(lua_State *L)
144121

145122
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
146123

147-
int idx = _to_positive_index(L, len, luaL_checkinteger(L, 2), compat_mode);
124+
int idx = luaSL_checkobjectindex(L, len, 2, compat_mode);
148125
if (idx < len && idx >= 0)
149126
{
150127
// This accessor does NOT auto-cast!
@@ -167,7 +144,7 @@ static int ll_getlistentrytype(lua_State *L)
167144

168145
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
169146

170-
int idx = _to_positive_index(L, len, luaL_checkinteger(L, 2), compat_mode);
147+
int idx = luaSL_checkobjectindex(L, len, 2, compat_mode);
171148
if (idx < len && idx >= 0)
172149
{
173150
luaSL_pushinteger(L, lua_lsl_type(&h->array[idx]));
@@ -194,8 +171,8 @@ static int ll_list2list(lua_State *L)
194171

195172
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
196173

197-
int target1 = _to_positive_index(L, len, luaL_checkunsigned(L, 2), compat_mode);
198-
int target2 = _to_positive_index(L, len, luaL_checkunsigned(L, 3), compat_mode);
174+
int target1 = luaSL_checkobjectindex(L, len, 2, compat_mode);
175+
int target2 = luaSL_checkobjectindex(L, len, 3, compat_mode);
199176

200177
int wanted_len = 0;
201178

@@ -337,8 +314,8 @@ static int ll_deletesublist(lua_State *L)
337314

338315
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
339316

340-
int target1 = _to_positive_index(L, len, luaL_checkunsigned(L, 2), compat_mode);
341-
int target2 = _to_positive_index(L, len, luaL_checkunsigned(L, 3), compat_mode);
317+
int target1 = luaSL_checkobjectindex(L, len, 2, compat_mode);
318+
int target2 = luaSL_checkobjectindex(L, len, 3, compat_mode);
342319

343320
if (target1 <= target2)
344321
{
@@ -420,7 +397,7 @@ static int ll_listinsertlist(lua_State *L)
420397

421398
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
422399

423-
int target = _to_positive_index(L, dest_len, luaL_checkunsigned(L, 3), compat_mode);
400+
int target = luaSL_checkobjectindex(L, dest_len, 3, compat_mode);
424401

425402
LuaTable *cloned_h = nullptr;
426403
TValue new_tv;
@@ -498,7 +475,7 @@ static int ll_listreplacelist(lua_State *L)
498475

499476
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
500477

501-
int target = _to_positive_index(L, orig_len, luaL_checkunsigned(L, 3), compat_mode);
478+
int target = luaSL_checkobjectindex(L, orig_len, 3, compat_mode);
502479

503480
LuaTable *cloned_h = nullptr;
504481
TValue new_tv;
@@ -813,7 +790,6 @@ static const luaL_Reg llcompateligiblelib[] = {
813790
{NULL, NULL},
814791
};
815792

816-
817793
// These are functions that may be used in tests or in the REPL,
818794
// but are not to be used in user-provided scripts
819795
static int ll_stringlength(lua_State *L)
@@ -824,14 +800,35 @@ static int ll_stringlength(lua_State *L)
824800

825801
static int ll_getsubstring(lua_State* L)
826802
{
827-
// Not even close to LSL semantics, also not memory-safe, but
828-
// useful for demo purposes. Should never be used outside of tests.
829-
// TODO: Make this crap use the utf8 <-> codepoint stuff.
830-
const auto *str_val = luaL_checkstring(L, 1);
831-
const auto start_idx = luaL_checkinteger(L, 2);
832-
const auto end_idx = luaL_checkinteger(L, 3);
803+
// UTF-8 aware substring extraction with 1-based indexing (SLua mode)
804+
// or 0-based indexing (LSL compat mode). Supports negative indices.
805+
// For demo/test purposes only, as it doesn't truncate at null like the real one.
806+
size_t str_len;
807+
const char *str_val = luaL_checklstring(L, 1, &str_len);
808+
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));
809+
810+
// Convert UTF-8 string to codepoints
811+
CodepointString codepoints = utf8str_to_codepoints(str_val, str_len);
812+
auto num_codes = (ptrdiff_t)codepoints.length();
833813

834-
lua_pushlstring(L, str_val + start_idx, end_idx - start_idx + 1);
814+
// Normalize indices (handles 1-based vs 0-based and negative indices)
815+
int start_idx = luaSL_checkobjectindex(L, (int)num_codes, 2, compat_mode);
816+
int end_idx = luaSL_checkobjectindex(L, (int)num_codes, 3, compat_mode);
817+
818+
// Bounds checking - return empty string if out of bounds
819+
if (start_idx < 0 || start_idx >= num_codes || end_idx < 0 || end_idx >= num_codes || start_idx > end_idx)
820+
{
821+
lua_pushstring(L, "");
822+
return 1;
823+
}
824+
825+
// Extract substring from codepoint array
826+
ptrdiff_t substr_len = end_idx - start_idx + 1;
827+
CodepointString substr = codepoints.substr(start_idx, substr_len);
828+
829+
// Convert back to UTF-8
830+
std::string result = codepoints_to_utf8str(substr, substr.length());
831+
lua_pushlstring(L, result.c_str(), result.length());
835832
return 1;
836833
}
837834

@@ -893,9 +890,13 @@ static int ll_generatekey(lua_State *L)
893890
return 1;
894891
}
895892

896-
static const luaL_Reg lltestlib[] = {
897-
// This requires weird unicode semantics we shouldn't re-implement by hand.
893+
// Test functions that need compat mode handling for index semantics
894+
static const luaL_Reg lltestcompateligiblelib[] = {
898895
{"GetSubString", ll_getsubstring},
896+
{NULL, NULL},
897+
};
898+
899+
static const luaL_Reg lltestlib[] = {
899900
{"OwnerSay", ll_ownersay},
900901
// We have to use LL's internal implementation because there are unicode
901902
// semantics we need to consider here.
@@ -922,6 +923,8 @@ int luaopen_ll(lua_State* L, int testing_funcs)
922923
// Pepper in some extra functions if we're testing
923924
luaL_register_noclobber(L, LUA_LLLIBNAME, lltestlib);
924925
lua_pop(L, 1);
926+
luaL_register_noclobber_compat(L, LUA_LLLIBNAME, lltestcompateligiblelib, true);
927+
lua_pop(L, 1);
925928
}
926929
}
927930
else
@@ -940,8 +943,12 @@ int luaopen_ll(lua_State* L, int testing_funcs)
940943
// Pepper in some extra functions if we're testing
941944
luaL_register_noclobber(L, LUA_LLLIBNAME, lltestlib);
942945
lua_pop(L, 1);
946+
luaL_register_noclobber_compat(L, LUA_LLLIBNAME, lltestcompateligiblelib, false);
947+
lua_pop(L, 1);
943948
luaL_register_noclobber(L, LUA_LLCOMPATLIBNAME, lltestlib);
944949
lua_pop(L, 1);
950+
luaL_register_noclobber_compat(L, LUA_LLCOMPATLIBNAME, lltestcompateligiblelib, true);
951+
lua_pop(L, 1);
945952
}
946953
}
947954
return 1;

VM/src/llsl.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,31 @@ int luaSL_checkindexlike(lua_State *L, int index)
13771377
return val;
13781378
}
13791379

1380+
int luaSL_checkobjectindex(lua_State *L, int len, int stack_idx, bool compat_mode)
1381+
{
1382+
int idx = luaL_checkinteger(L, stack_idx);
1383+
1384+
if (!compat_mode)
1385+
{
1386+
if (idx == 0)
1387+
{
1388+
luaL_error(L, "passed 0 when a 1-based index was expected");
1389+
}
1390+
else if (idx > 0)
1391+
{
1392+
// positive indices need to be shifted down to be 0-based.
1393+
// negative indices are handled later.
1394+
idx -= 1;
1395+
}
1396+
}
1397+
1398+
if(idx < 0)
1399+
{
1400+
return len + idx;
1401+
}
1402+
return idx;
1403+
}
1404+
13801405
void luaSL_pushboollike(lua_State *L, int val)
13811406
{
13821407
bool compat_mode = lua_toboolean(L, lua_upvalueindex(1));

VM/src/mono_strings.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <string>
44

5+
#include "mono_strings.h"
56

67
// Data table cribbed from Mono, see https://github.com/mono/mono/blob/mono-2.6.4/mono/metadata/char-conversions.h#L1589
78
static const uint16_t ToLowerDataLow [] = {
@@ -474,10 +475,7 @@ static size_t wchar_to_utf8chars(uint32_t in_char, char* outchars)
474475
return outchars - base;
475476
}
476477

477-
using CodepointString = std::basic_string<uint32_t>;
478-
479-
480-
static CodepointString utf8str_to_codepoints(const char *utf8str, size_t len)
478+
CodepointString utf8str_to_codepoints(const char *utf8str, size_t len)
481479
{
482480
CodepointString wout;
483481

@@ -565,7 +563,7 @@ static CodepointString utf8str_to_codepoints(const char *utf8str, size_t len)
565563
return wout;
566564
}
567565

568-
static std::string codepoints_to_utf8str(const CodepointString& utf32str, size_t len)
566+
std::string codepoints_to_utf8str(const CodepointString& utf32str, size_t len)
569567
{
570568
std::string out;
571569

VM/src/mono_strings.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
#include <string>
22

3+
using CodepointString = std::basic_string<char32_t>;
4+
5+
CodepointString utf8str_to_codepoints(const char *utf8str, size_t len);
6+
std::string codepoints_to_utf8str(const CodepointString& utf32str, size_t len);
7+
38
std::string to_upper_mono(const char *in, size_t len);
49
std::string to_lower_mono(const char *in, size_t len);

0 commit comments

Comments
 (0)