|
9 | 9 |
|
10 | 10 | #include "lprefix.h" |
11 | 11 |
|
| 12 | +#include <algorithm> |
| 13 | + |
12 | 14 | #include "lvirtualmachine.h" |
13 | 15 | #include "lapi.h" |
14 | 16 | #include "ldebug.h" |
@@ -61,6 +63,13 @@ inline void luai_threadyield(lua_State* L) noexcept { |
61 | 63 | #define vmcase(l) case l: |
62 | 64 | #define vmbreak break |
63 | 65 |
|
| 66 | +/* |
| 67 | +** Constants from lvm.cpp needed for VM operations |
| 68 | +*/ |
| 69 | + |
| 70 | +/* Limit for table tag-method (metamethod) chains to prevent infinite loops */ |
| 71 | +inline constexpr int MAXTAGLOOP = 2000; |
| 72 | + |
64 | 73 | /* |
65 | 74 | ** Arithmetic and bitwise helper functions for execute() |
66 | 75 | ** (copied from lvm.cpp since they're needed here now) |
@@ -1322,33 +1331,257 @@ lua_Integer VirtualMachine::shiftl(lua_Integer x, lua_Integer y) { |
1322 | 1331 | // === COMPARISONS === |
1323 | 1332 |
|
1324 | 1333 | int VirtualMachine::lessThan(const TValue *l, const TValue *r) { |
1325 | | - return luaV_lessthan(L, l, r); |
| 1334 | + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ |
| 1335 | + return *l < *r; /* Use operator< for cleaner syntax */ |
| 1336 | + else return L->lessThanOthers(l, r); |
1326 | 1337 | } |
1327 | 1338 |
|
1328 | 1339 | int VirtualMachine::lessEqual(const TValue *l, const TValue *r) { |
1329 | | - return luaV_lessequal(L, l, r); |
| 1340 | + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ |
| 1341 | + return *l <= *r; /* Use operator<= for cleaner syntax */ |
| 1342 | + else return L->lessEqualOthers(l, r); |
1330 | 1343 | } |
1331 | 1344 |
|
1332 | 1345 | int VirtualMachine::equalObj(const TValue *t1, const TValue *t2) { |
1333 | | - return luaV_equalobj(L, t1, t2); |
| 1346 | + const TValue *tm; |
| 1347 | + if (ttype(t1) != ttype(t2)) /* not the same type? */ |
| 1348 | + return 0; |
| 1349 | + else if (ttypetag(t1) != ttypetag(t2)) { |
| 1350 | + switch (ttypetag(t1)) { |
| 1351 | + case LuaT::NUMINT: { /* integer == float? */ |
| 1352 | + lua_Integer i2; |
| 1353 | + return (flttointeger(fltvalue(t2), &i2, F2Imod::F2Ieq) && |
| 1354 | + ivalue(t1) == i2); |
| 1355 | + } |
| 1356 | + case LuaT::NUMFLT: { /* float == integer? */ |
| 1357 | + lua_Integer i1; |
| 1358 | + return (flttointeger(fltvalue(t1), &i1, F2Imod::F2Ieq) && |
| 1359 | + i1 == ivalue(t2)); |
| 1360 | + } |
| 1361 | + case LuaT::SHRSTR: case LuaT::LNGSTR: { |
| 1362 | + return tsvalue(t1)->equals(tsvalue(t2)); |
| 1363 | + } |
| 1364 | + default: |
| 1365 | + return 0; |
| 1366 | + } |
| 1367 | + } |
| 1368 | + else { /* equal variants */ |
| 1369 | + switch (ttypetag(t1)) { |
| 1370 | + case LuaT::NIL: case LuaT::VFALSE: case LuaT::VTRUE: |
| 1371 | + return 1; |
| 1372 | + case LuaT::NUMINT: |
| 1373 | + return (ivalue(t1) == ivalue(t2)); |
| 1374 | + case LuaT::NUMFLT: |
| 1375 | + return (fltvalue(t1) == fltvalue(t2)); |
| 1376 | + case LuaT::LIGHTUSERDATA: return pvalue(t1) == pvalue(t2); |
| 1377 | + case LuaT::SHRSTR: |
| 1378 | + return eqshrstr(tsvalue(t1), tsvalue(t2)); |
| 1379 | + case LuaT::LNGSTR: |
| 1380 | + return tsvalue(t1)->equals(tsvalue(t2)); |
| 1381 | + case LuaT::USERDATA: { |
| 1382 | + if (uvalue(t1) == uvalue(t2)) return 1; |
| 1383 | + else if (L == nullptr) return 0; |
| 1384 | + tm = fasttm(L, uvalue(t1)->getMetatable(), TMS::TM_EQ); |
| 1385 | + if (tm == nullptr) |
| 1386 | + tm = fasttm(L, uvalue(t2)->getMetatable(), TMS::TM_EQ); |
| 1387 | + break; /* will try TM */ |
| 1388 | + } |
| 1389 | + case LuaT::TABLE: { |
| 1390 | + if (hvalue(t1) == hvalue(t2)) return 1; |
| 1391 | + else if (L == nullptr) return 0; |
| 1392 | + tm = fasttm(L, hvalue(t1)->getMetatable(), TMS::TM_EQ); |
| 1393 | + if (tm == nullptr) |
| 1394 | + tm = fasttm(L, hvalue(t2)->getMetatable(), TMS::TM_EQ); |
| 1395 | + break; /* will try TM */ |
| 1396 | + } |
| 1397 | + case LuaT::LCF: |
| 1398 | + return (fvalue(t1) == fvalue(t2)); |
| 1399 | + default: /* functions and threads */ |
| 1400 | + return (gcvalue(t1) == gcvalue(t2)); |
| 1401 | + } |
| 1402 | + if (tm == nullptr) /* no TM? */ |
| 1403 | + return 0; /* objects are different */ |
| 1404 | + else { |
| 1405 | + auto tag = luaT_callTMres(L, tm, t1, t2, L->getTop().p); /* call TM */ |
| 1406 | + return !tagisfalse(tag); |
| 1407 | + } |
| 1408 | + } |
1334 | 1409 | } |
1335 | 1410 |
|
1336 | 1411 | // === TABLE OPERATIONS === |
1337 | 1412 |
|
1338 | 1413 | LuaT VirtualMachine::finishGet(const TValue *t, TValue *key, StkId val, LuaT tag) { |
1339 | | - return luaV_finishget(L, t, key, val, tag); |
| 1414 | + const TValue *tm; /* metamethod */ |
| 1415 | + for (int loop = 0; loop < MAXTAGLOOP; loop++) { |
| 1416 | + if (tag == LuaT::NOTABLE) { /* 't' is not a table? */ |
| 1417 | + lua_assert(!ttistable(t)); |
| 1418 | + tm = luaT_gettmbyobj(L, t, TMS::TM_INDEX); |
| 1419 | + if (l_unlikely(notm(tm))) |
| 1420 | + luaG_typeerror(L, t, "index"); /* no metamethod */ |
| 1421 | + /* else will try the metamethod */ |
| 1422 | + } |
| 1423 | + else { /* 't' is a table */ |
| 1424 | + tm = fasttm(L, hvalue(t)->getMetatable(), TMS::TM_INDEX); /* table's metamethod */ |
| 1425 | + if (tm == nullptr) { /* no metamethod? */ |
| 1426 | + setnilvalue(s2v(val)); /* result is nil */ |
| 1427 | + return LuaT::NIL; |
| 1428 | + } |
| 1429 | + /* else will try the metamethod */ |
| 1430 | + } |
| 1431 | + if (ttisfunction(tm)) { /* is metamethod a function? */ |
| 1432 | + tag = luaT_callTMres(L, tm, t, key, val); /* call it */ |
| 1433 | + return tag; /* return tag of the result */ |
| 1434 | + } |
| 1435 | + t = tm; /* else try to access 'tm[key]' */ |
| 1436 | + tag = luaV_fastget(t, key, s2v(val), [](Table* tbl, const TValue* k, TValue* res) { return tbl->get(k, res); }); |
| 1437 | + if (!tagisempty(tag)) |
| 1438 | + return tag; /* done */ |
| 1439 | + /* else repeat (tail call 'luaV_finishget') */ |
| 1440 | + } |
| 1441 | + luaG_runerror(L, "'__index' chain too long; possible loop"); |
| 1442 | + return LuaT::NIL; /* to avoid warnings */ |
1340 | 1443 | } |
1341 | 1444 |
|
1342 | | -void VirtualMachine::finishSet(const TValue *t, TValue *key, TValue *val, int aux) { |
1343 | | - luaV_finishset(L, t, key, val, aux); |
| 1445 | +void VirtualMachine::finishSet(const TValue *t, TValue *key, TValue *val, int hres) { |
| 1446 | + for (int loop = 0; loop < MAXTAGLOOP; loop++) { |
| 1447 | + const TValue *tm; /* '__newindex' metamethod */ |
| 1448 | + if (hres != HNOTATABLE) { /* is 't' a table? */ |
| 1449 | + auto *h = hvalue(t); /* save 't' table */ |
| 1450 | + tm = fasttm(L, h->getMetatable(), TMS::TM_NEWINDEX); /* get metamethod */ |
| 1451 | + if (tm == nullptr) { /* no metamethod? */ |
| 1452 | + sethvalue2s(L, L->getTop().p, h); /* anchor 't' */ |
| 1453 | + L->getStackSubsystem().push(); /* assume EXTRA_STACK */ |
| 1454 | + h->finishSet(L, key, val, hres); /* set new value */ |
| 1455 | + L->getStackSubsystem().pop(); |
| 1456 | + invalidateTMcache(h); |
| 1457 | + luaC_barrierback(L, obj2gco(h), val); |
| 1458 | + return; |
| 1459 | + } |
| 1460 | + /* else will try the metamethod */ |
| 1461 | + } |
| 1462 | + else { /* not a table; check metamethod */ |
| 1463 | + tm = luaT_gettmbyobj(L, t, TMS::TM_NEWINDEX); |
| 1464 | + if (l_unlikely(notm(tm))) |
| 1465 | + luaG_typeerror(L, t, "index"); |
| 1466 | + } |
| 1467 | + /* try the metamethod */ |
| 1468 | + if (ttisfunction(tm)) { |
| 1469 | + luaT_callTM(L, tm, t, key, val); |
| 1470 | + return; |
| 1471 | + } |
| 1472 | + t = tm; /* else repeat assignment over 'tm' */ |
| 1473 | + hres = luaV_fastset(t, key, val, [](Table* tbl, const TValue* k, TValue* v) { return tbl->pset(k, v); }); |
| 1474 | + if (hres == HOK) { |
| 1475 | + luaV_finishfastset(L, t, val); |
| 1476 | + return; /* done */ |
| 1477 | + } |
| 1478 | + /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ |
| 1479 | + } |
| 1480 | + luaG_runerror(L, "'__newindex' chain too long; possible loop"); |
1344 | 1481 | } |
1345 | 1482 |
|
1346 | 1483 | // === STRING/OBJECT OPERATIONS === |
1347 | 1484 |
|
| 1485 | +/* Helper functions for string concatenation */ |
| 1486 | + |
| 1487 | +/* Function to ensure that element at 'o' is a string (converts if possible) */ |
| 1488 | +static inline bool tostring(lua_State* L, TValue* o) { |
| 1489 | + if (ttisstring(o)) return true; |
| 1490 | + if (!cvt2str(o)) return false; |
| 1491 | + luaO_tostring(L, o); |
| 1492 | + return true; |
| 1493 | +} |
| 1494 | + |
| 1495 | +static inline bool isemptystr(const TValue* o) noexcept { |
| 1496 | + return ttisshrstring(o) && tsvalue(o)->length() == 0; |
| 1497 | +} |
| 1498 | + |
| 1499 | +/* copy strings in stack from top - n up to top - 1 to buffer */ |
| 1500 | +static void copy2buff (StkId top, int n, char *buff) { |
| 1501 | + auto tl = size_t{0}; /* size already copied */ |
| 1502 | + do { |
| 1503 | + auto *st = tsvalue(s2v(top - n)); |
| 1504 | + size_t l; /* length of string being copied */ |
| 1505 | + auto *s = getlstr(st, l); |
| 1506 | + std::copy_n(s, l, buff + tl); |
| 1507 | + tl += l; |
| 1508 | + } while (--n > 0); |
| 1509 | +} |
| 1510 | + |
1348 | 1511 | void VirtualMachine::concat(int total) { |
1349 | | - luaV_concat(L, total); |
| 1512 | + if (total == 1) |
| 1513 | + return; /* "all" values already concatenated */ |
| 1514 | + do { |
| 1515 | + auto top = L->getTop().p; |
| 1516 | + auto n = 2; /* number of elements handled in this pass (at least 2) */ |
| 1517 | + if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || |
| 1518 | + !tostring(L, s2v(top - 1))) { |
| 1519 | + luaT_tryconcatTM(L); /* may invalidate 'top' */ |
| 1520 | + top = L->getTop().p; /* recapture after potential GC */ |
| 1521 | + } |
| 1522 | + else if (isemptystr(s2v(top - 1))) { /* second operand is empty? */ |
| 1523 | + cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ |
| 1524 | + top = L->getTop().p; /* recapture after potential GC */ |
| 1525 | + } |
| 1526 | + else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ |
| 1527 | + *s2v(top - 2) = *s2v(top - 1); /* result is second op. (operator=) */ |
| 1528 | + } |
| 1529 | + else { |
| 1530 | + /* at least two non-empty string values; get as many as possible */ |
| 1531 | + auto tl = tsslen(tsvalue(s2v(top - 1))); |
| 1532 | + TString *ts; |
| 1533 | + /* collect total length and number of strings */ |
| 1534 | + for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { |
| 1535 | + top = L->getTop().p; /* recapture after tostring() which can trigger GC */ |
| 1536 | + auto l = tsslen(tsvalue(s2v(top - n - 1))); |
| 1537 | + if (l_unlikely(l >= MAX_SIZE - sizeof(TString) - tl)) { |
| 1538 | + L->getStackSubsystem().setTopPtr(top - total); /* pop strings to avoid wasting stack */ |
| 1539 | + luaG_runerror(L, "string length overflow"); |
| 1540 | + } |
| 1541 | + tl += l; |
| 1542 | + } |
| 1543 | + if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ |
| 1544 | + char buff[LUAI_MAXSHORTLEN]; |
| 1545 | + copy2buff(top, n, buff); /* copy strings to buffer */ |
| 1546 | + ts = TString::create(L, buff, tl); |
| 1547 | + top = L->getTop().p; /* recapture after potential GC */ |
| 1548 | + } |
| 1549 | + else { /* long string; copy strings directly to final result */ |
| 1550 | + ts = TString::createLongString(L, tl); |
| 1551 | + top = L->getTop().p; /* recapture after potential GC */ |
| 1552 | + copy2buff(top, n, getlngstr(ts)); |
| 1553 | + } |
| 1554 | + setsvalue2s(L, top - n, ts); /* create result */ |
| 1555 | + } |
| 1556 | + total -= n - 1; /* got 'n' strings to create one new */ |
| 1557 | + L->getStackSubsystem().popN(n - 1); /* popped 'n' strings and pushed one */ |
| 1558 | + } while (total > 1); /* repeat until only 1 result left */ |
1350 | 1559 | } |
1351 | 1560 |
|
1352 | 1561 | void VirtualMachine::objlen(StkId ra, const TValue *rb) { |
1353 | | - luaV_objlen(L, ra, rb); |
| 1562 | + const TValue *tm; |
| 1563 | + switch (ttypetag(rb)) { |
| 1564 | + case LuaT::TABLE: { |
| 1565 | + Table *h = hvalue(rb); |
| 1566 | + tm = fasttm(L, h->getMetatable(), TMS::TM_LEN); |
| 1567 | + if (tm) break; /* metamethod? break switch to call it */ |
| 1568 | + s2v(ra)->setInt(l_castU2S(h->getn(L))); /* else primitive len */ |
| 1569 | + return; |
| 1570 | + } |
| 1571 | + case LuaT::SHRSTR: { |
| 1572 | + s2v(ra)->setInt(static_cast<lua_Integer>(tsvalue(rb)->length())); |
| 1573 | + return; |
| 1574 | + } |
| 1575 | + case LuaT::LNGSTR: { |
| 1576 | + s2v(ra)->setInt(cast_st2S(tsvalue(rb)->getLnglen())); |
| 1577 | + return; |
| 1578 | + } |
| 1579 | + default: { /* try metamethod */ |
| 1580 | + tm = luaT_gettmbyobj(L, rb, TMS::TM_LEN); |
| 1581 | + if (l_unlikely(notm(tm))) /* no metamethod? */ |
| 1582 | + luaG_typeerror(L, rb, "get length of"); |
| 1583 | + break; |
| 1584 | + } |
| 1585 | + } |
| 1586 | + luaT_callTMres(L, tm, rb, rb, ra); |
1354 | 1587 | } |
0 commit comments