Advertisement
sinkicol

sir hurt compiler source code XDDD

Jul 26th, 2020
235
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 71.83 KB | None | 0 0
  1. #include "Compiler.h"
  2. #include "ScriptParser.h"
  3. #include <sstream>
  4. #include "parallel_hashmap/phmap.h"
  5. #include <iostream>
  6. #include "ordered_map.h"
  7.  
  8. using namespace RBX;
  9.  
  10. #define MAX_REG_COUNT UCHAR_MAX
  11. #define MAX_LOCAL_COUNT UCHAR_MAX
  12.  
  13. template <typename ResultT, ResultT OffsetBasis, ResultT Prime>
  14. class basic_fnv1a final
  15. {
  16.  
  17.     static_assert(std::is_unsigned<ResultT>::value, "need unsigned integer");
  18.  
  19. public:
  20.  
  21.     using result_type = ResultT;
  22.  
  23. private:
  24.  
  25.     result_type state_{};
  26.  
  27. public:
  28.  
  29.     constexpr
  30.         basic_fnv1a() noexcept : state_{ OffsetBasis }
  31.     {
  32.     }
  33.  
  34.     constexpr void
  35.         update(const void *const data, const std::size_t size) noexcept
  36.     {
  37.         const auto cdata = static_cast<const unsigned char *>(data);
  38.         auto acc = this->state_;
  39.         for (auto i = std::size_t{}; i < size; ++i)
  40.         {
  41.             const auto next = std::size_t{ cdata[i] };
  42.             acc = (acc ^ next) * Prime;
  43.         }
  44.         this->state_ = acc;
  45.     }
  46.  
  47.     constexpr result_type
  48.         digest() const noexcept
  49.     {
  50.         return this->state_;
  51.     }
  52.  
  53. };
  54.  
  55. namespace Luau
  56. {
  57.     namespace ParserUtils
  58.     {
  59.         enum class RecurseDirection : bool
  60.         {
  61.             Left,
  62.             Right
  63.         };
  64.  
  65.         size_t getBinaryExprDepth(ScriptParser::AstExpr* expr,
  66.             ScriptParser::AstExprBinary::Op op, RecurseDirection direction, size_t c = 0)
  67.         {
  68.             ++c;
  69.  
  70.             auto binaryExpr = expr->as<ScriptParser::AstExprBinary>();
  71.             if (binaryExpr && binaryExpr->op == op)
  72.             {
  73.                 return getBinaryExprDepth(
  74.                     direction == RecurseDirection::Right ? binaryExpr->right : binaryExpr->left,
  75.                     op, direction, c);
  76.             }
  77.  
  78.             return c;
  79.         }
  80.  
  81.         size_t getIndexNameExprDepth(ScriptParser::AstExpr* expr, size_t c = 0)
  82.         {
  83.             ++c;
  84.  
  85.             if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
  86.             {
  87.                 return getIndexNameExprDepth(indexNameExpr->expr, c);
  88.             }
  89.  
  90.             return c;
  91.         }
  92.  
  93.         bool exprContainsGlobal(ScriptParser::AstExpr* expr)
  94.         {
  95.             if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
  96.             {
  97.                 return exprContainsGlobal(indexNameExpr->expr);
  98.             }
  99.  
  100.             return expr->is<ScriptParser::AstExprGlobal>();
  101.         }
  102.  
  103.         bool statBreaks(ScriptParser::AstStat* stat)
  104.         {
  105.             if (auto blockStat = stat->as<ScriptParser::AstStatBlock>())
  106.             {
  107.                 return blockStat->body.size == 1
  108.                     && blockStat->body.data[0]->is<ScriptParser::AstStatBreak>();
  109.             }
  110.  
  111.             return stat->is<ScriptParser::AstStatBreak>();
  112.         }
  113.  
  114.         bool statReturns(ScriptParser::AstStat* stat)
  115.         {
  116.             if (auto block = stat->as<ScriptParser::AstStatBlock>())
  117.             {
  118.                 return block->body.size ? statReturns(block->body.data[block->body.size - 1]) : false;
  119.             }
  120.  
  121.             return stat->is<ScriptParser::AstStatReturn>();
  122.         }
  123.     }
  124.  
  125.     class Compiler
  126.     {
  127.         bool debugInfo;
  128.  
  129.         std::vector<std::string> strings;
  130.         std::vector<std::pair<ScriptParser::AstExprFunction*,
  131.             ScriptParser::AstStat*>> funcExprs;
  132.  
  133.         struct AssignmentVisitor : ScriptParser::AstVisitor
  134.         {
  135.  
  136.         } assignmentVisitor{};
  137.  
  138.         // collects constant values
  139.         class ConstantVisitor : public ScriptParser::AstVisitor
  140.         {
  141.             Compiler& c;
  142.  
  143.             void addStringEntry(const std::string& str)
  144.             {
  145.                 auto it = std::find(c.strings.begin(), c.strings.end(), str);
  146.                 if (it == c.strings.end())
  147.                 {
  148.                     c.strings.push_back(str);
  149.                 }
  150.             }
  151.         public:
  152.             ConstantVisitor(Compiler& c) : c(c) {}
  153.  
  154.             bool visit(ScriptParser::AstExprConstantString* stringExpr) override
  155.             {
  156.                 addStringEntry({ stringExpr->value.data, stringExpr->value.size });
  157.  
  158.                 return false;
  159.             }
  160.  
  161.             bool visit(ScriptParser::AstExprGlobal* globalExpr) override
  162.             {
  163.                 addStringEntry({ globalExpr->name.value });
  164.  
  165.                 return false;
  166.             }
  167.  
  168.             bool visit(ScriptParser::AstExprIndexName* indexNameExpr) override
  169.             {
  170.                 indexNameExpr->expr->visit(this);
  171.                 addStringEntry({ indexNameExpr->index.value });
  172.  
  173.                 return false;
  174.             }
  175.  
  176.             bool visit(ScriptParser::AstExprCall* callExpr) override
  177.             {
  178.                 ScriptParser::AstExprIndexName* indexNameExpr = nullptr;
  179.                 if (callExpr->self)
  180.                 {
  181.                     if ((indexNameExpr =
  182.                         callExpr->func->as<ScriptParser::AstExprIndexName>()))
  183.                     {
  184.                         indexNameExpr->expr->visit(this);
  185.                     }
  186.                     else
  187.                     {
  188.                         callExpr->func->visit(this);
  189.                     }
  190.                 }
  191.                 else
  192.                 {
  193.                     callExpr->func->visit(this);
  194.                 }
  195.  
  196.                 for (auto a : callExpr->args)
  197.                 {
  198.                     a->visit(this);
  199.                 }
  200.  
  201.                 if (indexNameExpr)
  202.                 {
  203.                     addStringEntry({ indexNameExpr->index.value });
  204.                 }
  205.  
  206.                 return false;
  207.             }
  208.  
  209.             bool visit(ScriptParser::AstStatRepeat* repeatStat)
  210.             {
  211.                 repeatStat->body->visit(this);
  212.                 repeatStat->condition->visit(this);
  213.  
  214.                 return false;
  215.             }
  216.  
  217.             // TODO: move to assignment visitor? check roblox studio
  218.             // TODO: unsure whether this is right. should test further
  219.             bool visit(ScriptParser::AstStatAssign* assignStat) override
  220.             {
  221.                 for (auto val : assignStat->values)
  222.                 {
  223.                     val->visit(this);
  224.                 }
  225.  
  226.                 for (auto var : assignStat->vars)
  227.                 {
  228.                     var->visit(this);
  229.                 }
  230.  
  231.                 return false;
  232.             }
  233.         } constantVisitor{ *this };
  234.  
  235.         // collects function prototypes
  236.         class FunctionVisitor : public ScriptParser::AstVisitor
  237.         {
  238.             Compiler& c;
  239.         public:
  240.             FunctionVisitor(Compiler& c) : c(c) {}
  241.  
  242.             bool visit(ScriptParser::AstStatLocal* localStat) override
  243.             {
  244.                 if (localStat->usingFunctionSugar)
  245.                 {
  246.                     auto funcExpr =
  247.                         localStat->values.data[0]->as<ScriptParser::AstExprFunction>();
  248.                     if (funcExpr)
  249.                     {
  250.                         funcExpr->body->visit(this);
  251.  
  252.                         c.funcExprs.emplace_back(funcExpr, localStat);
  253.                         return false;
  254.                     }
  255.                 }
  256.  
  257.                 for (auto val : localStat->values)
  258.                 {
  259.                     val->visit(this);
  260.                 }
  261.  
  262.                 return false;
  263.             }
  264.  
  265.             bool visit(ScriptParser::AstStatAssign* assignStat) override
  266.             {
  267.                 if (assignStat->usingFunctionSugar)
  268.                 {
  269.                     auto funcExpr =
  270.                         assignStat->values.data[0]->as<ScriptParser::AstExprFunction>();
  271.                     if (funcExpr)
  272.                     {
  273.                         funcExpr->body->visit(this);
  274.  
  275.                         c.funcExprs.emplace_back(funcExpr, assignStat);
  276.                         return false;
  277.                     }
  278.                 }
  279.  
  280.                 for (auto val : assignStat->values)
  281.                 {
  282.                     val->visit(this);
  283.                 }
  284.  
  285.                 return false;
  286.             }
  287.  
  288.             bool visit(ScriptParser::AstExprFunction* funcExpr) override
  289.             {
  290.                 funcExpr->body->visit(this);
  291.  
  292.                 c.funcExprs.emplace_back(funcExpr, nullptr);
  293.  
  294.                 return false;
  295.             }
  296.         } functionVisitor{ *this };
  297.  
  298.         enum class OpCode : byte
  299.         {
  300.             Nop,
  301.             SaveCode,
  302.             LoadNil,
  303.             LoadBool, // A B C  R(A) := (Bool)B; pc += C
  304.             LoadShort,
  305.             LoadConst,
  306.             Move,
  307.             GetGlobal,
  308.             SetGlobal,
  309.             GetUpvalue,
  310.             SetUpvalue,
  311.             SaveRegisters,
  312.             GetGlobalConst,
  313.             GetTableIndex,
  314.             SetTableIndex,
  315.             GetTableIndexConstant,
  316.             SetTableIndexConstant,
  317.             GetTableIndexByte,
  318.             SetTableIndexByte,
  319.             Closure,
  320.             Self,
  321.             Call,
  322.             Return,
  323.             Jump,
  324.             LoopJump,
  325.             Test,
  326.             NotTest,
  327.             Equal,
  328.             LesserOrEqual,
  329.             LesserThan,
  330.             NotEqual,
  331.             GreaterThan,
  332.             GreaterOrEqual,
  333.             Add,
  334.             Sub,
  335.             Mul,
  336.             Div,
  337.             Mod,
  338.             Pow,
  339.             AddByte,
  340.             SubByte,
  341.             MulByte,
  342.             DivByte,
  343.             ModByte,
  344.             PowByte,
  345.             Or,
  346.             And,
  347.             OrByte,
  348.             AndByte,
  349.             Concat,
  350.             Not,
  351.             UnaryMinus,
  352.             Len,
  353.             NewTable,
  354.             NewTableConst,
  355.             SetList,
  356.             ForPrep,
  357.             ForLoop,
  358.             TForLoop,
  359.             LoopJumpIPairs,
  360.             TForLoopIPairs,
  361.             LoopJumpNext,
  362.             TForLoopNext,
  363.             LoadVarargs,
  364.             ClearStack,
  365.             ClearStackFull,
  366.             LoadConstLarge,
  367.             FarJump,
  368.             BuiltinCall
  369.         };
  370.  
  371.         static constexpr byte getClientOp(OpCode op)
  372.         {
  373.             return 227 - (29 * (byte(op) - 1));
  374.         }
  375.  
  376.         struct Instruction32
  377.         {
  378.             union
  379.             {
  380.                 uint32_t encoded = 0;
  381.                 struct
  382.                 {
  383.                     OpCode op;
  384.                     byte a;
  385.                     union
  386.                     {
  387.                         struct
  388.                         {
  389.                             byte b;
  390.                             byte c;
  391.                         };
  392.                         uint16_t b_x;
  393.                         int16_t s_b_x;
  394.                     };
  395.                 };
  396.             };
  397.             bool extraData = false;
  398.  
  399.             Instruction32(uint32_t encoded)
  400.                 : encoded(encoded), extraData(true) {}
  401.  
  402.             Instruction32(OpCode op, byte a, byte b, byte c)
  403.                 : op(op), a(a), b(b), c(c) {}
  404.  
  405.             Instruction32(OpCode op, byte a, uint16_t b_x)
  406.                 : op(op), a(a), b_x(b_x) {}
  407.  
  408.             Instruction32(OpCode op, byte a, int16_t s_b_x)
  409.                 : op(op), a(a), s_b_x(s_b_x) {}
  410.         };
  411.  
  412.         enum class ConstantType : byte
  413.         {
  414.             ConstantNil,
  415.             ConstantBoolean,
  416.             ConstantNumber,
  417.             ConstantString,
  418.             ConstantGlobal,
  419.             ConstantHashTable
  420.         };
  421.  
  422.         struct Constant
  423.         {
  424.             virtual ~Constant() = default;
  425.  
  426.             virtual ConstantType getType() = 0;
  427.             virtual void writeData(ByteStream& buffer) = 0;
  428.  
  429.             virtual bool equals(Constant* rhs) = 0;
  430.         };
  431.  
  432.         struct ConstantNil : Constant
  433.         {
  434.             ConstantType getType() override
  435.             {
  436.                 return ConstantType::ConstantNil;
  437.             }
  438.  
  439.             void writeData(ByteStream& buffer) override {}
  440.  
  441.             bool equals(Constant* rhs) override
  442.             {
  443.                 return true;
  444.             }
  445.         };
  446.  
  447.         struct ConstantBoolean : Constant
  448.         {
  449.             bool value;
  450.  
  451.             ConstantBoolean(bool value) : value(value) {}
  452.  
  453.             ConstantType getType() override
  454.             {
  455.                 return ConstantType::ConstantBoolean;
  456.             }
  457.  
  458.             void writeData(ByteStream& buffer) override
  459.             {
  460.                 buffer << value;
  461.             }
  462.  
  463.             bool equals(Constant* rhs) override
  464.             {
  465.                 if (auto other = dynamic_cast<ConstantBoolean*>(rhs))
  466.                 {
  467.                     return value == other->value;
  468.                 }
  469.                 return false;
  470.             }
  471.         };
  472.  
  473.         struct ConstantNumber : Constant
  474.         {
  475.             double value;
  476.  
  477.             ConstantNumber(double value) : value(value) {}
  478.  
  479.             ConstantType getType() override
  480.             {
  481.                 return ConstantType::ConstantNumber;
  482.             }
  483.  
  484.             void writeData(ByteStream& buffer) override
  485.             {
  486.                 buffer << value;
  487.             }
  488.  
  489.             bool equals(Constant* rhs) override
  490.             {
  491.                 if (auto other = dynamic_cast<ConstantNumber*>(rhs))
  492.                 {
  493.                     return value == other->value;
  494.                 }
  495.                 return false;
  496.             }
  497.         };
  498.  
  499.         struct ConstantString : Constant
  500.         {
  501.             std::string value;
  502.             Compiler& c;
  503.  
  504.             ConstantString(const std::string& value, Compiler& c) : value(value), c(c)
  505.             {
  506.                 auto& strings = c.strings;
  507.                 auto it = std::find(strings.begin(), strings.end(), value);
  508.                 if (it == strings.end())
  509.                     strings.push_back(value);
  510.             }
  511.  
  512.             ConstantType getType() override
  513.             {
  514.                 return ConstantType::ConstantString;
  515.             }
  516.  
  517.             void writeData(ByteStream& buffer) override
  518.             {
  519.                 auto& strings = c.strings;
  520.  
  521.                 auto it = std::find_if(strings.begin(), strings.end(),
  522.                     [&](const std::string& str)
  523.                     {
  524.                         return str == value;
  525.                     });
  526.                 if (it == strings.end())
  527.                     throw runtime_error("could not find string in strings table.");
  528.  
  529.                 buffer << int(std::distance(strings.begin(), it) + 1);
  530.             }
  531.  
  532.             bool equals(Constant* rhs) override
  533.             {
  534.                 if (auto other = dynamic_cast<ConstantString*>(rhs))
  535.                 {
  536.                     return value == other->value;
  537.                 }
  538.                 return false;
  539.             }
  540.         };
  541.  
  542.         struct ConstantGlobal : Constant
  543.         {
  544.             uint32_t idx;
  545.  
  546.             ConstantGlobal(uint32_t idx) : idx(idx) {}
  547.  
  548.             ConstantType getType() override
  549.             {
  550.                 return ConstantType::ConstantGlobal;
  551.             }
  552.  
  553.             void writeData(ByteStream& buffer) override
  554.             {
  555.                 buffer << idx;
  556.             }
  557.  
  558.             bool equals(Constant* rhs) override
  559.             {
  560.                 if (auto other = dynamic_cast<ConstantGlobal*>(rhs))
  561.                 {
  562.                     return idx == other->idx;
  563.                 }
  564.                 return false;
  565.             }
  566.         };
  567.  
  568.         struct ConstantHashTable : Constant
  569.         {
  570.             std::vector<uint32_t> stringIndices;
  571.  
  572.             ConstantHashTable() = default;
  573.  
  574.             ConstantType getType() override
  575.             {
  576.                 return ConstantType::ConstantHashTable;
  577.             }
  578.  
  579.             void writeData(ByteStream& buffer) override
  580.             {
  581.                 buffer << int(stringIndices.size());
  582.                 for (auto i : stringIndices)
  583.                 {
  584.                     // pretty sure this is an int (based on minimal reversing)
  585.                     buffer << int(i);
  586.                 }
  587.             }
  588.  
  589.             bool equals(Constant* rhs) override
  590.             {
  591.                 if (auto other = dynamic_cast<ConstantHashTable*>(rhs))
  592.                 {
  593.                     // checking size equality first is probably more efficient? idk
  594.                     return stringIndices.size() == other->stringIndices.size()
  595.                         && stringIndices == other->stringIndices;
  596.                 }
  597.                 return false;
  598.             }
  599.         };
  600.  
  601.         struct FunctionPrototype
  602.         {
  603.             byte regCount = 0;
  604.             byte maxRegCount = 0;
  605.             byte argCount = 0;
  606.             byte isVarArg = 0;
  607.  
  608.             std::optional<std::string> name;
  609.  
  610.             std::map<std::string, byte> passedUpvals;
  611.             std::vector<std::string> upvals;
  612.             std::vector<uint32_t> code;
  613.             phmap::flat_hash_map<std::string, byte> locals;
  614.             std::vector<Constant*> constants;
  615.             unsigned int lastLine = 0;
  616.             std::vector<size_t> lineInfo;
  617.             std::vector<std::string> modifiedGlobals;
  618.             std::vector<ScriptParser::AstExprFunction*> closures;
  619.             std::vector<size_t> jumpOutQueue;
  620.             byte loopDepth = 0;
  621.  
  622.             ScriptParser::Allocator& a;
  623.             Compiler& compiler;
  624.  
  625.             FunctionPrototype(ScriptParser::Allocator& a, Compiler& compiler) : a(a), compiler(compiler) {}
  626.  
  627.             byte allocateRegisters(byte c)
  628.             {
  629.                 auto ret = regCount;
  630.                 if (regCount + c > MAX_REG_COUNT)
  631.                 {
  632.                     throw runtime_error(
  633.                         "Out of registers when trying to allocate %d registers: exceeded limit %d",
  634.                         c, MAX_REG_COUNT);
  635.                 }
  636.  
  637.                 regCount += c;
  638.  
  639.                 if (regCount > maxRegCount)
  640.                     maxRegCount = regCount;
  641.  
  642.                 return ret;
  643.             }
  644.  
  645.             byte freeRegisters(byte c)
  646.             {
  647.                 if (regCount - c < 0)
  648.                 {
  649.                     throw runtime_error(
  650.                         "Attempt to deallocate %d registers: action puts registers below minimum 0",
  651.                         c);
  652.                 }
  653.  
  654.                 regCount -= c;
  655.  
  656.                 return regCount;
  657.             }
  658.  
  659.             void assignLocal(int reg_idx, const std::string& name)
  660.             {
  661.                 if (locals.size() > MAX_LOCAL_COUNT)
  662.                     throw runtime_error("Out of local variables: exceeded limit %d",
  663.                         MAX_LOCAL_COUNT);
  664.  
  665.                 locals[name] = reg_idx;
  666.             }
  667.  
  668.             int getOrCreateClosure(ScriptParser::AstExprFunction* funcExpr)
  669.             {
  670.                 auto it = std::find(closures.begin(), closures.end(), funcExpr);
  671.                 if (it == closures.end())
  672.                 {
  673.                     closures.push_back(funcExpr);
  674.                     return closures.size() - 1;
  675.                 }
  676.  
  677.                 return std::distance(closures.begin(), it);
  678.             }
  679.  
  680.             static uint32_t encodeGlobalConstantIndex(uint32_t idx)
  681.             {
  682.                 return (idx | 0x400) << 20;
  683.             }
  684.  
  685.             static uint32_t encodeGlobalConstantIndex(uint32_t idx, uint32_t idx1)
  686.             {
  687.                 return (idx | 0x800) << 20 | idx1 << 10;
  688.             }
  689.  
  690.             static uint32_t encodeGlobalConstantIndex(uint32_t idx, uint32_t idx1, uint32_t idx2)
  691.             {
  692.                 return (idx | 0xC00) << 20 | idx1 << 10 | idx2;
  693.             }
  694.  
  695.             uint32_t hashString(const char* str)
  696.             {
  697.                 uint32_t result = strlen(str);
  698.                 uint32_t v3 = (result >> 5) + 1;
  699.                 for (auto i = strlen(str); i >= v3; i -= v3)
  700.                 {
  701.                     auto v4 = (unsigned __int8)str[i - 1];
  702.                     result ^= (result >> 2) + 32 * result + v4;
  703.                 }
  704.                 return result;
  705.             }
  706.  
  707.             uint32_t getOrCreateConstant(ScriptParser::AstExpr* constExpr)
  708.             {
  709.                 if (constExpr->is<ScriptParser::AstExprConstantNil>())
  710.                 {
  711.                     return getOrCreateConstant(new (a) ConstantNil{});
  712.                 }
  713.  
  714.                 if (auto boolConstExpr = constExpr->as<ScriptParser::AstExprConstantBool>())
  715.                 {
  716.                     return getOrCreateConstant(new (a) ConstantBoolean{ boolConstExpr->value });
  717.                 }
  718.  
  719.                 if (auto numConstExpr = constExpr->as<ScriptParser::AstExprConstantNumber>())
  720.                 {
  721.                     return getOrCreateConstant(new (a) ConstantNumber{ numConstExpr->value });
  722.                 }
  723.  
  724.                 if (auto stringConstExpr = constExpr->as<ScriptParser::AstExprConstantString>())
  725.                 {
  726.                     return getOrCreateConstant(new (a) ConstantString{
  727.                         {stringConstExpr->value.data, stringConstExpr->value.size}, compiler
  728.                         });
  729.                 }
  730.  
  731.                 if (auto globalConstExpr = constExpr->as<ScriptParser::AstExprGlobal>())
  732.                 {
  733.                     auto constantIdx = getOrCreateConstant(new (a)
  734.                         ConstantString{ globalConstExpr->name.value, compiler });
  735.                     auto encodedConstantIdx = encodeGlobalConstantIndex(constantIdx);
  736.  
  737.                     return getOrCreateConstant(new (a) ConstantGlobal{ encodedConstantIdx });
  738.                 }
  739.  
  740.                 return -1;
  741.             }
  742.  
  743.             uint32_t getOrCreateConstant(Constant* constant)
  744.             {
  745.                 auto it = std::find_if(constants.begin(), constants.end(),
  746.                     [&](Constant* c)
  747.                     {
  748.                         return constant->equals(c);
  749.                     });
  750.  
  751.                 if (it != constants.end())
  752.                     return std::distance(constants.begin(), it);
  753.  
  754.                 constants.push_back(constant);
  755.  
  756.                 return constants.size() - 1;
  757.             }
  758.  
  759.             uint32_t getOrCreateConstant(double val)
  760.             {
  761.                 auto constant = new (a) ConstantNumber{ val };
  762.  
  763.                 auto it = std::find_if(constants.begin(), constants.end(),
  764.                     [&](Constant* c)
  765.                     {
  766.                         return constant->equals(c);
  767.                     });
  768.  
  769.                 if (it != constants.end())
  770.                 {
  771.                     return std::distance(constants.begin(), it);
  772.                 }
  773.  
  774.                 constants.push_back(constant);
  775.  
  776.                 return constants.size() - 1;
  777.             }
  778.  
  779.             uint32_t getOrCreateConstant(ScriptParser::AstArray<char> val)
  780.             {
  781.                 auto constant = new (a) ConstantString{ std::string{ val.data, val.size }, compiler };
  782.  
  783.                 auto it = std::find_if(constants.begin(), constants.end(),
  784.                     [&](Constant* c)
  785.                     {
  786.                         return constant->equals(c);
  787.                     });
  788.  
  789.                 if (it != constants.end())
  790.                 {
  791.                     return std::distance(constants.begin(), it);
  792.                 }
  793.  
  794.                 constants.push_back(constant);
  795.  
  796.                 return constants.size() - 1;
  797.             }
  798.  
  799.             void writeInstruction32(const Instruction32& instr,
  800.                 const ScriptParser::Position& pos)
  801.             {
  802.                 lineInfo.push_back(pos.line - lastLine);
  803.                 lastLine = pos.line;
  804.  
  805. #ifndef _DEBUG
  806.                 if (!instr.extraData)
  807.                 {
  808.                     Instruction32 instrCopy = instr;
  809.                     instrCopy.op = OpCode(getClientOp(instr.op));
  810.                     code.push_back(instrCopy.encoded);
  811.                 }
  812.                 else
  813. #endif
  814.                 {
  815.                     code.push_back(instr.encoded);
  816.                 }
  817.             }
  818.  
  819.             void writeInstruction32(const Instruction32& instr)
  820.             {
  821.                 lineInfo.push_back(0);
  822.  
  823. #ifndef _DEBUG
  824.                 if (!instr.extraData)
  825.                 {
  826.                     Instruction32 instrCopy = instr;
  827.                     instrCopy.op = OpCode(getClientOp(instr.op));
  828.                     code.push_back(instrCopy.encoded);
  829.                 }
  830.                 else
  831. #endif
  832.                 {
  833.                     code.push_back(instr.encoded);
  834.                 }
  835.             }
  836.  
  837.             void compileIfStat(ScriptParser::AstStatIf* ifStat)
  838.             {
  839.                 auto conditionExpr = ifStat->condition;
  840.                 while (auto groupExpr = conditionExpr->as<ScriptParser::AstExprGroup>())
  841.                 {
  842.                     conditionExpr = groupExpr->expr;
  843.                 }
  844.  
  845.                 // NOTE: now handled in parser
  846.                 /*
  847.                 auto boolExpr = conditionExpr->as<ScriptParser::AstExprConstantBool>();
  848.                 if (conditionExpr->is<ScriptParser::AstExprConstantNumber>()
  849.                     || (boolExpr
  850.                         && boolExpr->value))
  851.                 {
  852.                     compileStatement(ifStat->thenbody);
  853.                     locals = localsBackup;
  854.                     return;
  855.                 }
  856.                  */
  857.  
  858.                 if (!ifStat->elsebody && ParserUtils::statBreaks(ifStat->thenbody)
  859.                     && loopDepth != 0)
  860.                 {
  861.                     compileConditionExpr(conditionExpr, nullptr, jumpOutQueue, true);
  862.                     return;
  863.                 }
  864.  
  865.                 std::vector<size_t> jumpIndicies;
  866.                 compileConditionExpr(conditionExpr, nullptr, jumpIndicies, false);
  867.                 compileStatement(ifStat->thenbody);
  868.  
  869.                 if (ifStat->elsebody)
  870.                 {
  871.                     if (ParserUtils::statReturns(ifStat->thenbody))
  872.                     {
  873.                         calculateJumps(jumpIndicies, code.size());
  874.                         compileStatement(ifStat->elsebody);
  875.                     }
  876.                     else
  877.                     {
  878.                         auto thenJumpIndex = code.size();
  879.                         writeInstruction32({ OpCode::Jump, 0, int16_t(0) },
  880.                             ifStat->thenbody->location.end);
  881.  
  882.                         calculateJumps(jumpIndicies, code.size());
  883.                         compileStatement(ifStat->elsebody);
  884.  
  885.                         calculateJump(thenJumpIndex, code.size());
  886.                     }
  887.                 }
  888.                 else
  889.                 {
  890.                     calculateJumps(jumpIndicies, code.size());
  891.                 }
  892.             }
  893.  
  894.             void compileWhileStat(ScriptParser::AstStatWhile* whileStat)
  895.             {
  896.                 auto conditionExpr = whileStat->condition;
  897.                 while (auto groupExpr = conditionExpr->as<ScriptParser::AstExprGroup>())
  898.                 {
  899.                     conditionExpr = groupExpr->expr;
  900.                 }
  901.  
  902.                 auto jumpOutQueueStart = jumpOutQueue.size();
  903.                 size_t loopEndIndex;
  904.  
  905.                 auto constEvalRes = conditionExpr->constEval();
  906.                 if (constEvalRes == ScriptParser::ConstEvalResult::True)
  907.                 {
  908.                     auto loopStartIndex = code.size();
  909.                     auto localsBackup = locals;
  910.                     loopDepth++;
  911.                     compileStatement(whileStat->body);
  912.                     loopDepth--;
  913.                     locals = localsBackup;
  914.  
  915.                     auto loopJumpBackIndex = code.size();
  916.                     writeInstruction32({ OpCode::LoopJump, 0, int16_t(0) },
  917.                         whileStat->location.end);
  918.  
  919.                     loopEndIndex = code.size();
  920.                     calculateJump(loopJumpBackIndex, loopStartIndex);
  921.                 }
  922.                 else if (constEvalRes == ScriptParser::ConstEvalResult::Invalid)
  923.                 {
  924.                     auto loopStartIndex = code.size();
  925.  
  926.                     std::vector<size_t> jumpIndicies;
  927.                     compileConditionExpr(conditionExpr, nullptr, jumpIndicies, false);
  928.  
  929.                     auto localsBackup = locals;
  930.                     loopDepth++;
  931.                     compileStatement(whileStat->body);
  932.                     loopDepth--;
  933.                     locals = localsBackup;
  934.  
  935.                     auto loopJumpBackIndex = code.size();
  936.                     writeInstruction32({ OpCode::LoopJump, 0, int16_t(0) },
  937.                         whileStat->location.end);
  938.                     calculateJump(loopJumpBackIndex, loopStartIndex);
  939.  
  940.                     loopEndIndex = code.size();
  941.                     calculateJumps(jumpIndicies, loopEndIndex);
  942.                 }
  943.                 else
  944.                 {
  945.                     return;
  946.                 }
  947.  
  948.                 for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
  949.                 {
  950.                     calculateJump(jumpOutQueue.at(i), loopEndIndex);
  951.                 }
  952.                 jumpOutQueue.resize(jumpOutQueueStart);
  953.             }
  954.  
  955.             void compileConditionExpr(ScriptParser::AstExpr* conditionExpr, byte* reg,
  956.                 std::vector<size_t>& relocations, bool invert)
  957.             {
  958.  
  959.                 if (auto binaryExpr = conditionExpr->as<ScriptParser::AstExprBinary>())
  960.                 {
  961.                     switch (binaryExpr->op)
  962.                     {
  963.                     case ScriptParser::AstExprBinary::Add: break;
  964.                     case ScriptParser::AstExprBinary::Sub: break;
  965.                     case ScriptParser::AstExprBinary::Mul: break;
  966.                     case ScriptParser::AstExprBinary::Div: break;
  967.                     case ScriptParser::AstExprBinary::Mod: break;
  968.                     case ScriptParser::AstExprBinary::Pow: break;
  969.                     case ScriptParser::AstExprBinary::Concat: break;
  970.                     case ScriptParser::AstExprBinary::CompareNe:
  971.                     case ScriptParser::AstExprBinary::CompareEq:
  972.                     case ScriptParser::AstExprBinary::CompareLt:
  973.                     case ScriptParser::AstExprBinary::CompareLe:
  974.                     case ScriptParser::AstExprBinary::CompareGt:
  975.                     case ScriptParser::AstExprBinary::CompareGe:
  976.                         relocations.push_back(compileCompareExpr(binaryExpr, !invert));
  977.                         return;
  978.                     case ScriptParser::AstExprBinary::And:
  979.                     case ScriptParser::AstExprBinary::Or:
  980.                     {
  981.                         if (invert == (binaryExpr->op == ScriptParser::AstExprBinary::And))
  982.                         {
  983.                             std::vector<size_t> tempRelocations;
  984.  
  985.                             compileConditionExpr(binaryExpr->left, nullptr, tempRelocations,
  986.                                 !invert);
  987.                             compileConditionExpr(binaryExpr->right, reg, relocations, invert);
  988.  
  989.                             calculateJumps(tempRelocations, code.size());
  990.                         }
  991.                         else
  992.                         {
  993.                             compileConditionExpr(binaryExpr->left, reg, relocations, invert);
  994.                             compileConditionExpr(binaryExpr->right, reg, relocations, invert);
  995.                         }
  996.                         return;
  997.                     }
  998.                     default:;
  999.                     }
  1000.                 }
  1001.                 if (auto unaryExpr = conditionExpr->as<ScriptParser::AstExprUnary>())
  1002.                 {
  1003.                     if (unaryExpr->op == ScriptParser::AstExprUnary::Not)
  1004.                     {
  1005.                         return compileConditionExpr(unaryExpr->expr, reg, relocations, !invert);
  1006.                     }
  1007.                 }
  1008.  
  1009.                 auto regBackup = regCount;
  1010.  
  1011.                 byte conditionReg;
  1012.                 if (reg)
  1013.                 {
  1014.                     conditionReg = *reg;
  1015.                     compileExpression(conditionExpr, conditionReg);
  1016.                 }
  1017.                 else
  1018.                 {
  1019.                     conditionReg =
  1020.                         getLocalOrCompileExpression(conditionExpr, allocateRegisters(1));
  1021.                 }
  1022.  
  1023.                 relocations.push_back(code.size());
  1024.                 writeInstruction32({ invert ? OpCode::Test : OpCode::NotTest, conditionReg,
  1025.                     int16_t(0) }, conditionExpr->location.end);
  1026.  
  1027.                 regCount = regBackup;
  1028.  
  1029.                 return;
  1030.             }
  1031.  
  1032.             // returns true if compiled as a tail expression
  1033.             bool compileTailExpression(ScriptParser::AstExpr* expr, byte reg)
  1034.             {
  1035.                 if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
  1036.                 {
  1037.                     compileCallExpr(callExpr, reg, 0, false, true);
  1038.                     return true;
  1039.                 }
  1040.  
  1041.                 if (expr->is<ScriptParser::AstExprVarargs>())
  1042.                 {
  1043.                     writeInstruction32({ OpCode::LoadVarargs, reg, 0, 0 }, expr->location.begin);
  1044.                     return true;
  1045.                 }
  1046.  
  1047.                 compileExpression(expr, reg);
  1048.                 return false;
  1049.             }
  1050.  
  1051.             void compileExpression(ScriptParser::AstExpr* expr, byte reg)
  1052.             {
  1053.                 if (auto groupExpr = expr->as<ScriptParser::AstExprGroup>())
  1054.                 {
  1055.                     compileExpression(groupExpr->expr, reg);
  1056.                 }
  1057.                 else if (expr->is<ScriptParser::AstExprConstantNil>())
  1058.                 {
  1059.                     writeInstruction32({ OpCode::LoadNil, reg, 0, 0 }, expr->location.begin);
  1060.                 }
  1061.                 else if (auto boolExpr = expr->as<ScriptParser::AstExprConstantBool>())
  1062.                 {
  1063.                     writeInstruction32({ OpCode::LoadBool, reg, boolExpr->value, 0 },
  1064.                         expr->location.begin);
  1065.                 }
  1066.                 else if (auto numExpr = expr->as<ScriptParser::AstExprConstantNumber>())
  1067.                 {
  1068.                     if (numExpr->value <= SHRT_MAX
  1069.                         && numExpr->value >= SHRT_MIN
  1070.                         && floor(numExpr->value) == numExpr->value) // is whole
  1071.                     {
  1072.                         writeInstruction32({ OpCode::LoadShort, reg, int16_t(numExpr->value) },
  1073.                             expr->location.begin);
  1074.                         return;
  1075.                     }
  1076.  
  1077.                     auto constIndex = getOrCreateConstant(numExpr->value);
  1078.                     if (constIndex <= UINT16_MAX)
  1079.                     {
  1080.                         writeInstruction32({ OpCode::LoadConst, reg,
  1081.                             uint16_t(constIndex) }, expr->location.begin);
  1082.                     }
  1083.                     else
  1084.                     {
  1085.                         writeInstruction32({ OpCode::LoadConstLarge, reg, uint16_t(0) },
  1086.                             expr->location.begin);
  1087.                         writeInstruction32(constIndex);
  1088.                     }
  1089.  
  1090.  
  1091.                 }
  1092.                 else if (auto strExpr = expr->as<ScriptParser::AstExprConstantString>())
  1093.                 {
  1094.                     auto constIndex = getOrCreateConstant(strExpr->value);
  1095.                     if (constIndex <= UINT16_MAX)
  1096.                     {
  1097.                         writeInstruction32({ OpCode::LoadConst, reg,
  1098.                             uint16_t(constIndex) }, expr->location.begin);
  1099.                     }
  1100.                     else
  1101.                     {
  1102.                         writeInstruction32({ OpCode::LoadConstLarge, reg, uint16_t(0) },
  1103.                             expr->location.begin);
  1104.                         writeInstruction32(constIndex);
  1105.                     }
  1106.                 }
  1107.                 else if (auto localExpr = expr->as<ScriptParser::AstExprLocal>())
  1108.                 {
  1109.                     compileLocalExpr(localExpr, reg);
  1110.                 }
  1111.                 else if (auto globalExpr = expr->as<ScriptParser::AstExprGlobal>())
  1112.                 {
  1113.                     compileGlobalExpr(globalExpr, reg);
  1114.                 }
  1115.                 else if (auto varargsExpr = expr->as<ScriptParser::AstExprVarargs>())
  1116.                 {
  1117.                     writeInstruction32({ OpCode::LoadVarargs, reg, 2, 0 },
  1118.                         varargsExpr->location.begin);
  1119.                 }
  1120.                 else if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
  1121.                 {
  1122.                     compileCallExpr(callExpr, reg, 1, false, false);
  1123.                 }
  1124.                 else if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
  1125.                 {
  1126.                     compileIndexNameExpr(indexNameExpr, reg);
  1127.                 }
  1128.                 else if (auto indexExpr = expr->as<ScriptParser::AstExprIndexExpr>())
  1129.                 {
  1130.                     compileIndexExpr(indexExpr, reg);
  1131.                 }
  1132.                 else if (auto funcExpr = expr->as<ScriptParser::AstExprFunction>())
  1133.                 {
  1134.                     compileFunctionExprRef(funcExpr, reg);
  1135.                 }
  1136.                 else if (auto tableExpr = expr->as<ScriptParser::AstExprTable>())
  1137.                 {
  1138.                     compileTableExpr(tableExpr, reg);
  1139.                 }
  1140.                 else if (auto unaryExpr = expr->as<ScriptParser::AstExprUnary>())
  1141.                 {
  1142.                     compileUnaryExpr(unaryExpr, reg);
  1143.                 }
  1144.                 else if (auto binaryExpr = expr->as<ScriptParser::AstExprBinary>())
  1145.                 {
  1146.                     compileBinaryExpr(binaryExpr, reg);
  1147.                 }
  1148.             }
  1149.  
  1150.             byte getLocalOrCompileExpression(ScriptParser::AstExpr* expr, byte reg)
  1151.             {
  1152.                 if (auto localExpr = expr->as<ScriptParser::AstExprLocal>())
  1153.                 {
  1154.                     auto it = locals.find(localExpr->local->name.value);
  1155.                     if (it != locals.end())
  1156.                     {
  1157.                         return it->second;
  1158.                     }
  1159.                 }
  1160.  
  1161.                 compileExpression(expr, reg);
  1162.                 return reg;
  1163.             }
  1164.  
  1165.             void compileBinaryExpr(ScriptParser::AstExprBinary* binaryExpr, byte reg)
  1166.             {
  1167.                 switch (binaryExpr->op)
  1168.                 {
  1169.                 case ScriptParser::AstExprBinary::Add:
  1170.                 case ScriptParser::AstExprBinary::Sub:
  1171.                 case ScriptParser::AstExprBinary::Mul:
  1172.                 case ScriptParser::AstExprBinary::Div:
  1173.                 case ScriptParser::AstExprBinary::Mod:
  1174.                 case ScriptParser::AstExprBinary::Pow:
  1175.                 {
  1176.                     auto lhs = getLocalOrCompileExpression(binaryExpr->left, reg);
  1177.                     auto rhs = binaryExpr->right;
  1178.  
  1179.                     // yay optimization
  1180.                     /*if (auto numExpr = rhs->as<ScriptParser::AstExprConstantNumber>();
  1181.                         numExpr
  1182.                         && floor(numExpr->value) == numExpr->value
  1183.                         && numExpr->value < UCHAR_MAX)
  1184.                     {
  1185.                         static OpCode binaryOpByteMap[6] {
  1186.                             OpCode::AddByte, OpCode::SubByte, OpCode::MulByte,
  1187.                             OpCode::DivByte, OpCode::ModByte, OpCode::PowByte
  1188.                         };
  1189.                         writeInstruction32({ binaryOpByteMap[binaryExpr->op], reg, lhs,
  1190.                             byte(numExpr->value) }, binaryExpr->location.begin);
  1191.                     }
  1192.                     else*/
  1193.                     {
  1194.                         static OpCode binaryOpMap[6]{
  1195.                             OpCode::Add, OpCode::Sub, OpCode::Mul,
  1196.                             OpCode::Div, OpCode::Mod, OpCode::Pow
  1197.                         };
  1198.  
  1199.                         auto rhsReg = getLocalOrCompileExpression(rhs, allocateRegisters(1));
  1200.  
  1201.                         writeInstruction32({ binaryOpMap[binaryExpr->op], reg, lhs,
  1202.                              rhsReg }, binaryExpr->location.begin);
  1203.  
  1204.                         freeRegisters(1);
  1205.                     }
  1206.                     break;
  1207.                 }
  1208.                 case ScriptParser::AstExprBinary::Concat:
  1209.                 {
  1210.                     auto concatDepth = ParserUtils::getBinaryExprDepth(binaryExpr,
  1211.                         ScriptParser::AstExprBinary::Concat, ParserUtils::RecurseDirection::Right);
  1212.                     if (concatDepth > 2)
  1213.                     {
  1214.                         auto regBackup = regCount;
  1215.  
  1216.                         auto c = binaryExpr;
  1217.                         while (c != nullptr
  1218.                             && c->op == ScriptParser::AstExprBinary::Concat)
  1219.                         {
  1220.                             compileExpression(c->left, allocateRegisters(1));
  1221.                             if (auto next = c->right->as<ScriptParser::AstExprBinary>())
  1222.                             {
  1223.                                 c = next;
  1224.                             }
  1225.                             else
  1226.                             {
  1227.                                 break;
  1228.                             }
  1229.                         }
  1230.                         compileExpression(c->right, allocateRegisters(1));
  1231.  
  1232.                         writeInstruction32({ OpCode::Concat, reg,
  1233.                             byte(reg + 1), byte(reg + concatDepth) });
  1234.  
  1235.                         regCount = regBackup;
  1236.                         break;
  1237.                     }
  1238.  
  1239.                     compileExpression(binaryExpr->left, allocateRegisters(1));
  1240.                     compileExpression(binaryExpr->right, allocateRegisters(1));
  1241.  
  1242.                     writeInstruction32({ OpCode::Concat, reg,
  1243.                         byte(reg + 1), byte(reg + 2) });
  1244.  
  1245.                     freeRegisters(2);
  1246.                     break;
  1247.                 }
  1248.                 case ScriptParser::AstExprBinary::CompareNe:
  1249.                 case ScriptParser::AstExprBinary::CompareEq:
  1250.                 case ScriptParser::AstExprBinary::CompareLt:
  1251.                 case ScriptParser::AstExprBinary::CompareLe:
  1252.                 case ScriptParser::AstExprBinary::CompareGt:
  1253.                 case ScriptParser::AstExprBinary::CompareGe:
  1254.                 {
  1255.                     auto compIndex = compileCompareExpr(binaryExpr, false);
  1256.                     writeInstruction32({ OpCode::LoadBool, reg, 0, 1 },
  1257.                         binaryExpr->location.begin);
  1258.                     auto trueCaseIndex = code.size();
  1259.                     writeInstruction32({ OpCode::LoadBool, reg, 1, 0 },
  1260.                         binaryExpr->location.begin);
  1261.                     calculateJump(compIndex, trueCaseIndex);
  1262.                     break;
  1263.                 }
  1264.                 case ScriptParser::AstExprBinary::And:
  1265.                 case ScriptParser::AstExprBinary::Or:
  1266.                 {
  1267.                     compileLogicalBinaryExpr(binaryExpr, reg);
  1268.                     break;
  1269.                 }
  1270.                 }
  1271.             }
  1272.  
  1273.             void compileLogicalBinaryExpr(ScriptParser::AstExprBinary* binaryExpr, byte reg)
  1274.             {
  1275.                 /*auto depth = ParserUtils::getBinaryExprDepth(binaryExpr,
  1276.                     binaryExpr->op, ParserUtils::RecurseDirection::Left);
  1277.                 if (depth > 2)
  1278.                 {
  1279.                     std::vector<ScriptParser::AstExpr*> exprList;
  1280.                     auto c = binaryExpr;
  1281.                     while (true)
  1282.                     {
  1283.                         exprList.push_back(c->right);
  1284.                         if (auto next = c->left->as<ScriptParser::AstExprBinary>())
  1285.                         {
  1286.                             c = next;
  1287.                         }
  1288.                         else
  1289.                         {
  1290.                             break;
  1291.                         }
  1292.                     }
  1293.                     exprList.push_back(c->left);
  1294.                     std::reverse(exprList.begin(), exprList.end());
  1295.                     std::vector<size_t> testIndexList;
  1296.                     testIndexList.reserve(exprList.size() - 1);
  1297.                     for (auto it = exprList.cbegin();; ++it)
  1298.                     {
  1299.                         compileExpression(*it, reg);
  1300.                         if (it + 1 == exprList.cend())
  1301.                         {
  1302.                             break;
  1303.                         }
  1304.                         testIndexList.push_back(code.size());
  1305.                         writeInstruction32({
  1306.                             binaryExpr->op == ScriptParser::AstExprBinary::And
  1307.                             ? OpCode::NotTest : OpCode::Test, reg, int16_t(0) },
  1308.                             binaryExpr->left->location.end);
  1309.                     }
  1310.                     auto endIndex = code.size();
  1311.                     for (auto testIndex : testIndexList)
  1312.                     {
  1313.                         calculateJump(testIndex, endIndex);
  1314.                     }
  1315.                     return;
  1316.                 }*/
  1317.  
  1318.                 compileExpression(binaryExpr->left, reg);
  1319.  
  1320.                 auto testIndex = code.size();
  1321.                 writeInstruction32({
  1322.                     binaryExpr->op == ScriptParser::AstExprBinary::And
  1323.                     ? OpCode::NotTest : OpCode::Test, reg, int16_t(0) },
  1324.                     binaryExpr->left->location.end);
  1325.  
  1326.                 compileExpression(binaryExpr->right, reg);
  1327.  
  1328.                 auto endIndex = code.size();
  1329.  
  1330.                 calculateJump(testIndex, endIndex);
  1331.             }
  1332.  
  1333.             void calculateJump(size_t fromIndex, size_t toIndex)
  1334.             {
  1335.                 int jumpDistance = toIndex - fromIndex - 1;
  1336.                 if ((int16_t)jumpDistance == jumpDistance)
  1337.                 {
  1338.                     Instruction32 instr = code[fromIndex];
  1339.                     instr.b_x = (uint16_t)jumpDistance;
  1340.                     code[fromIndex] = instr.encoded;
  1341.                 }
  1342.                 else
  1343.                 {
  1344.                     // if (abs(jumpDistance) >= 0x800000)
  1345.                     {
  1346.                         throw RBX::runtime_error("Jump distance is too large: %d",
  1347.                             jumpDistance);
  1348.                     }
  1349.  
  1350.                     // TODO: big jumps
  1351.                 }
  1352.             }
  1353.  
  1354.             void calculateJumps(const std::vector<size_t> fromIndicies, size_t toIndex)
  1355.             {
  1356.                 for (auto fromIndex : fromIndicies)
  1357.                 {
  1358.                     calculateJump(fromIndex, toIndex);
  1359.                 }
  1360.             }
  1361.  
  1362.             size_t compileCompareExpr(ScriptParser::AstExprBinary* binaryExpr, bool invert)
  1363.             {
  1364.                 auto regBackup = regCount;
  1365.  
  1366.                 auto leftReg = getLocalOrCompileExpression(binaryExpr->left, allocateRegisters(1));
  1367.                 auto rightReg = getLocalOrCompileExpression(binaryExpr->right, allocateRegisters(1));
  1368.  
  1369.                 OpCode op = OpCode::Nop;
  1370.                 switch (binaryExpr->op)
  1371.                 {
  1372.                 case ScriptParser::AstExprBinary::CompareNe:
  1373.                     op = OpCode::NotEqual;
  1374.                     if (invert)
  1375.                         op = OpCode::Equal;
  1376.                     break;
  1377.                 case ScriptParser::AstExprBinary::CompareEq:
  1378.                     op = OpCode::Equal;
  1379.                     if (invert)
  1380.                         op = OpCode::NotEqual;
  1381.                     break;
  1382.                 case ScriptParser::AstExprBinary::CompareLt:
  1383.                 case ScriptParser::AstExprBinary::CompareGt:
  1384.                     op = OpCode::LesserThan;
  1385.                     if (invert)
  1386.                         op = OpCode::GreaterOrEqual;
  1387.                     break;
  1388.                 case ScriptParser::AstExprBinary::CompareLe:
  1389.                 case ScriptParser::AstExprBinary::CompareGe:
  1390.                     op = OpCode::LesserOrEqual;
  1391.                     if (invert)
  1392.                         op = OpCode::GreaterThan;
  1393.                     break;
  1394.                 default:;
  1395.                 }
  1396.  
  1397.                 auto compIndex = code.size();
  1398.  
  1399.                 if (binaryExpr->op == ScriptParser::AstExprBinary::CompareGt
  1400.                     || binaryExpr->op == ScriptParser::AstExprBinary::CompareGe)
  1401.                 {
  1402.                     writeInstruction32({ op, rightReg, int16_t(0) },
  1403.                         binaryExpr->location.begin);
  1404.                     writeInstruction32(leftReg,
  1405.                         binaryExpr->location.begin);
  1406.                 }
  1407.                 else
  1408.                 {
  1409.                     writeInstruction32({ op, leftReg, int16_t(0) },
  1410.                         binaryExpr->location.begin);
  1411.                     writeInstruction32(rightReg,
  1412.                         binaryExpr->location.begin);
  1413.                 }
  1414.  
  1415.                 regCount = regBackup;
  1416.  
  1417.                 return compIndex;
  1418.             }
  1419.  
  1420.             void compileUnaryExpr(ScriptParser::AstExprUnary* unaryExpr, byte reg)
  1421.             {
  1422.                 static OpCode unaryOpMap[3]{
  1423.                     OpCode::Not, OpCode::UnaryMinus, OpCode::Len
  1424.                 };
  1425.  
  1426.                 if (unaryExpr->op == ScriptParser::AstExprUnary::Not)
  1427.                 {
  1428.                     if (unaryExpr->expr->is<ScriptParser::AstExprConstantNumber>())
  1429.                     {
  1430.                         writeInstruction32({ OpCode::LoadBool, reg, 0, 0 },
  1431.                             unaryExpr->expr->location.begin);
  1432.                         return;
  1433.                     }
  1434.  
  1435.                     if (unaryExpr->expr->is<ScriptParser::AstExprConstantNil>())
  1436.                     {
  1437.                         writeInstruction32({ OpCode::LoadBool, reg, 1, 0 },
  1438.                             unaryExpr->expr->location.begin);
  1439.                         return;
  1440.                     }
  1441.  
  1442.                     if (auto boolExpr = unaryExpr->expr->as<ScriptParser::AstExprConstantBool>())
  1443.                     {
  1444.                         writeInstruction32({ OpCode::LoadBool, reg, !boolExpr->value, 0 },
  1445.                             unaryExpr->expr->location.begin);
  1446.                         return;
  1447.                     }
  1448.                 }
  1449.  
  1450.                 auto regBackup = regCount;
  1451.  
  1452.                 writeInstruction32({ unaryOpMap[unaryExpr->op], reg,
  1453.                     getLocalOrCompileExpression(unaryExpr->expr, allocateRegisters(1)), 0 },
  1454.                     unaryExpr->location.begin);
  1455.  
  1456.                 regCount = regBackup;
  1457.             }
  1458.  
  1459.             void compileTableExpr(ScriptParser::AstExprTable* tableExpr, byte reg)
  1460.             {
  1461.                 if (tableExpr->pairs.size == 0)
  1462.                 {
  1463.                     writeInstruction32({ OpCode::NewTable, reg, 0, 0 },
  1464.                         tableExpr->location.begin);
  1465.                     writeInstruction32(0);
  1466.                     return;
  1467.                 }
  1468.  
  1469.                 bool useHashOptimization = true;
  1470.                 byte hashSize = 0;
  1471.                 size_t arraySize = 0;
  1472.                 for (size_t i = 0; i < tableExpr->pairs.size; i += 2)
  1473.                 {
  1474.                     if (auto key = tableExpr->pairs.data[i])
  1475.                     {
  1476.                         if (!key->is<ScriptParser::AstExprConstantString>())
  1477.                         {
  1478.                             useHashOptimization = false;
  1479.                         }
  1480.                         hashSize++;
  1481.                     }
  1482.                     else
  1483.                     {
  1484.                         useHashOptimization = false;
  1485.                         arraySize++;
  1486.                     }
  1487.                 }
  1488.  
  1489.                 if (useHashOptimization)
  1490.                 {
  1491.                     auto hashTable = new (a) ConstantHashTable{};
  1492.                     for (auto i = 0; i < hashSize * 2; i += 2)
  1493.                     {
  1494.                         auto stringConst =
  1495.                             tableExpr->pairs.data[i]->as<ScriptParser::AstExprConstantString>();
  1496.  
  1497.                         auto stringConstValue =
  1498.                             std::string{ stringConst->value.data, stringConst->value.size };
  1499.  
  1500.                         auto stringConstIdx = getOrCreateConstant(new (a)
  1501.                             ConstantString{ stringConstValue, compiler });
  1502.                         hashTable->stringIndices.push_back(stringConstIdx);
  1503.                     }
  1504.  
  1505.                     auto constIndex = getOrCreateConstant(hashTable);
  1506.                     if (constIndex > UINT16_MAX)
  1507.                     {
  1508.                         goto normal;
  1509.                     }
  1510.  
  1511.                     writeInstruction32({ OpCode::NewTableConst, reg,
  1512.                         uint16_t(constIndex) }, tableExpr->location.begin);
  1513.  
  1514.                     for (auto i = 0; i < hashSize * 2; i += 2)
  1515.                     {
  1516.                         auto stringConst =
  1517.                             tableExpr->pairs.data[i]->as<ScriptParser::AstExprConstantString>();
  1518.  
  1519.                         auto stringConstValue =
  1520.                             std::string{ stringConst->value.data, stringConst->value.size };
  1521.  
  1522.                         auto stringConstIdx = getOrCreateConstant(new (a)
  1523.                             ConstantString{ stringConstValue, compiler });
  1524.  
  1525.                         auto valReg = allocateRegisters(1);
  1526.  
  1527.                         compileExpression(tableExpr->pairs.data[i + 1], valReg);
  1528.                         writeInstruction32({ OpCode::SetTableIndexConstant, valReg,
  1529.                             reg, byte(hashString(stringConstValue.c_str())) });
  1530.                         writeInstruction32({ stringConstIdx });
  1531.  
  1532.                         freeRegisters(1);
  1533.                     }
  1534.                     return;
  1535.                 }
  1536.             normal:
  1537.  
  1538.                 byte encodedHashSize = 0;
  1539.                 do
  1540.                 {
  1541.                     encodedHashSize++;
  1542.                 } while (1 << encodedHashSize < hashSize);
  1543.  
  1544.                 writeInstruction32({ OpCode::NewTable, reg,
  1545.                     byte(hashSize ? encodedHashSize + 1 : 0), 0 },
  1546.                     tableExpr->location.begin);
  1547.  
  1548.                 writeInstruction32(arraySize);
  1549.  
  1550.                 auto regBackup = regCount;
  1551.  
  1552.                 size_t arrayIndex = 0;
  1553.                 size_t flushIndex = 0;
  1554.                 size_t insertLocation = 1;
  1555.                 byte arrayInc = std::min<byte>(16, MAX_REG_COUNT - regCount);
  1556.                 bool wasTail = false;
  1557.                 for (size_t i = 0; i < tableExpr->pairs.size; i += 2)
  1558.                 {
  1559.                     auto key = tableExpr->pairs.data[i];
  1560.                     auto val = tableExpr->pairs.data[i + 1];
  1561.  
  1562.                     if (key)
  1563.                     {
  1564.                         auto keyReg = allocateRegisters(1);
  1565.                         auto valReg = allocateRegisters(1);
  1566.  
  1567.                         compileExpression(key, keyReg);
  1568.                         compileExpression(val, valReg);
  1569.  
  1570.                         writeInstruction32({ OpCode::SetTableIndex, valReg, reg, keyReg },
  1571.                             key->location.begin);
  1572.  
  1573.                         freeRegisters(2);
  1574.                     }
  1575.                     else if (i + 2 == tableExpr->pairs.size)
  1576.                     {
  1577.                         wasTail = compileTailExpression(val, allocateRegisters(1));
  1578.  
  1579.                         flushIndex++;
  1580.                         arrayIndex++;
  1581.                     }
  1582.                     else
  1583.                     {
  1584.                         compileExpression(val, allocateRegisters(1));
  1585.  
  1586.                         flushIndex++;
  1587.                         arrayIndex++;
  1588.  
  1589.                         if (arrayIndex % arrayInc == 0)
  1590.                         {
  1591.                             auto count = byte(arrayInc);
  1592.  
  1593.                             if (!wasTail)
  1594.                             {
  1595.                                 count++;
  1596.                             }
  1597.  
  1598.                             writeInstruction32({ OpCode::SetList, reg, byte(reg + 1),
  1599.                                 byte(wasTail ? 0 : count) });
  1600.                             writeInstruction32(insertLocation);
  1601.  
  1602.                             insertLocation = arrayIndex + 1;
  1603.                             flushIndex = 0;
  1604.                             regCount = regBackup;
  1605.                         }
  1606.                     }
  1607.                 }
  1608.  
  1609.                 if (flushIndex)
  1610.                 {
  1611.                     auto count = byte(flushIndex + 1);
  1612.  
  1613.                     writeInstruction32({ OpCode::SetList, reg, byte(reg + 1),
  1614.                         byte(wasTail ? 0 : count) });
  1615.                     writeInstruction32(insertLocation);
  1616.                 }
  1617.  
  1618.                 regCount = regBackup;
  1619.             }
  1620.  
  1621.             void compileLocalExpr(ScriptParser::AstExprLocal* localExpr, byte reg, bool set = false)
  1622.             {
  1623.                 if (localExpr->upvalue) // is possibly an upvalue
  1624.                 {
  1625.                     // locals are prioritized over upvalues
  1626.                     auto it = locals.find(localExpr->local->name.value);
  1627.  
  1628.                     if (it != locals.end())
  1629.                     {
  1630.                         if (set)
  1631.                         {
  1632.                             writeInstruction32({ OpCode::Move, it->second,
  1633.                                 reg, 0 },
  1634.                                 localExpr->location.begin);
  1635.                         }
  1636.                         else
  1637.                         {
  1638.                             writeInstruction32({ OpCode::Move, reg,
  1639.                                 it->second, 0 },
  1640.                                 localExpr->location.begin);
  1641.                         }
  1642.                     }
  1643.                     else
  1644.                     {
  1645.                         auto upvalIt =
  1646.                             std::find(upvals.begin(), upvals.end(),
  1647.                                 localExpr->local->name.value);
  1648.  
  1649.                         if (upvalIt == upvals.end())
  1650.                         {
  1651.                             upvals.push_back(localExpr->local->name.value);
  1652.                             upvalIt = upvals.end() - 1;
  1653.                         }
  1654.  
  1655.  
  1656.  
  1657.                         if (set)
  1658.                         {
  1659.                             writeInstruction32({ OpCode::SetUpvalue, reg,
  1660.                                 byte(std::distance(upvals.begin(), upvalIt)), 0 },
  1661.                                 localExpr->location.begin);
  1662.                         }
  1663.                         else
  1664.                         {
  1665.                             writeInstruction32({ OpCode::GetUpvalue, reg,
  1666.                                 byte(std::distance(upvals.begin(), upvalIt)), 0 },
  1667.                                 localExpr->location.begin);
  1668.                         }
  1669.                     }
  1670.                 }
  1671.                 else
  1672.                 {
  1673.                     if (!set)
  1674.                     {
  1675.                         writeInstruction32({ OpCode::Move, reg,
  1676.                             locals.at(localExpr->local->name.value), 0 },
  1677.                             localExpr->location.begin);
  1678.                     }
  1679.                     else
  1680.                     {
  1681.                         writeInstruction32({ OpCode::Move, locals.at(localExpr->local->name.value),
  1682.                             reg, 0 },
  1683.                             localExpr->location.begin);
  1684.                     }
  1685.                 }
  1686.             }
  1687.  
  1688.             void compileFunctionExprRef(ScriptParser::AstExprFunction* funcExpr, byte reg)
  1689.             {
  1690.                 auto closureIdx = getOrCreateClosure(funcExpr);
  1691.  
  1692.                 writeInstruction32({ OpCode::Closure, reg, uint16_t(closureIdx) },
  1693.                     funcExpr->location.begin);
  1694.  
  1695.                 auto proto = compiler.protos.at(funcExpr);
  1696.                 for (auto upval : proto.upvals)
  1697.                 {
  1698.                     auto it = locals.find(upval);
  1699.  
  1700.                     if (it != locals.end())
  1701.                     {
  1702.                         writeInstruction32({ OpCode::Move, 0,
  1703.                             it->second, 0 },
  1704.                             funcExpr->location.begin);
  1705.  
  1706.                         passedUpvals[upval] = it->second;
  1707.                     }
  1708.                     else
  1709.                     {
  1710.                         auto upvalIt =
  1711.                             std::find(upvals.begin(), upvals.end(),
  1712.                                 upval);
  1713.  
  1714.                         if (upvalIt == upvals.end())
  1715.                         {
  1716.                             upvals.push_back(upval);
  1717.                             upvalIt = upvals.end() - 1;
  1718.                         }
  1719.  
  1720.                         writeInstruction32({ OpCode::GetUpvalue, 0,
  1721.                             byte(std::distance(upvals.begin(), upvalIt)), 0 },
  1722.                             funcExpr->location.begin);
  1723.                     }
  1724.                 }
  1725.             }
  1726.  
  1727.             void compileExpressionIndexOptimized(ScriptParser::AstExpr* expr, byte reg)
  1728.             {
  1729.                 // faster to have common expressions at the top
  1730.                 if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
  1731.                 {
  1732.                     compileIndexNameExpr(indexNameExpr, reg);
  1733.                 }
  1734.                 else if (auto indexExpr = expr->as<ScriptParser::AstExprIndexExpr>())
  1735.                 {
  1736.                     compileIndexExpr(indexExpr, reg);
  1737.                 }
  1738.                 else if (auto globalExpr = expr->as<ScriptParser::AstExprGlobal>())
  1739.                 {
  1740.                     compileGlobalExpr(globalExpr, reg);
  1741.                 }
  1742.                 else if (auto localExpr = expr->as<ScriptParser::AstExprLocal>())
  1743.                 {
  1744.                     compileLocalExpr(localExpr, reg);
  1745.                 }
  1746.                 else
  1747.                 {
  1748.                     compileExpression(expr, reg);
  1749.                 }
  1750.             }
  1751.  
  1752.             void compileIndexExpr(ScriptParser::AstExprIndexExpr* indexExpr, byte reg)
  1753.             {
  1754.                 auto regBackup = regCount;
  1755.                 auto indexReg = getLocalOrCompileExpression(indexExpr->expr, allocateRegisters(1));
  1756.  
  1757.                 auto numConstExpr = indexExpr->index->as<ScriptParser::AstExprConstantNumber>();
  1758.                 if (numConstExpr
  1759.                     && floor(numConstExpr->value) == numConstExpr->value
  1760.                     && numConstExpr->value >= 1
  1761.                     && numConstExpr->value <= UCHAR_MAX + 1)
  1762.                 {
  1763.                     writeInstruction32({ OpCode::GetTableIndexByte, reg, indexReg,
  1764.                         byte(numConstExpr->value - 1) }, numConstExpr->location.begin);
  1765.                 }
  1766.                 else
  1767.                 {
  1768.                     auto indexValue = getLocalOrCompileExpression(indexExpr->index, allocateRegisters(1));
  1769.  
  1770.                     writeInstruction32({ OpCode::GetTableIndex, reg, indexReg, indexValue },
  1771.                         indexExpr->index->location.begin);
  1772.                 }
  1773.  
  1774.                 regCount = regBackup;
  1775.             }
  1776.  
  1777.             void compileIndexNameExpr(ScriptParser::AstExprIndexName* indexNameExpr, byte reg)
  1778.             {
  1779.                 if (ParserUtils::getIndexNameExprDepth(indexNameExpr) <= 3
  1780.                     && ParserUtils::exprContainsGlobal(indexNameExpr))
  1781.                 {
  1782.                     std::vector<std::string> indexStrings{};
  1783.                     ScriptParser::AstExprIndexName* c = indexNameExpr;
  1784.                     while (c != nullptr)
  1785.                     {
  1786.                         indexStrings.push_back(c->index.value);
  1787.  
  1788.                         if (auto globalExpr = c->expr->as<ScriptParser::AstExprGlobal>())
  1789.                         {
  1790.                             indexStrings.push_back(globalExpr->name.value);
  1791.                             break;
  1792.                         }
  1793.  
  1794.                         c = c->expr->as<ScriptParser::AstExprIndexName>();
  1795.                     }
  1796.  
  1797.                     // check if builtin
  1798.                     static auto LuauBuiltinGlobals = std::array<std::string, 8>
  1799.                     {
  1800.                         "Game", "Workspace", "_G",
  1801.                             "game", "plugin", "script",
  1802.                             "shared", "workspace"
  1803.                     };
  1804.  
  1805.                     auto bgIt = std::find(LuauBuiltinGlobals.cbegin(),
  1806.                         LuauBuiltinGlobals.cend(), indexStrings.back());
  1807.  
  1808.                     if (bgIt == LuauBuiltinGlobals.cend())
  1809.                     {
  1810.                         std::reverse(indexStrings.begin(), indexStrings.end());
  1811.  
  1812.                         auto constantIdx1 = getOrCreateConstant(new (a)
  1813.                             ConstantString{ indexStrings[0], compiler });
  1814.  
  1815.                         auto constantIdx2 = getOrCreateConstant(new (a)
  1816.                             ConstantString{ indexStrings[1], compiler });
  1817.  
  1818.                         uint32_t encodedConstIdx;
  1819.  
  1820.                         if (indexStrings.size() == 2)
  1821.                         {
  1822.                             encodedConstIdx = encodeGlobalConstantIndex(constantIdx1,
  1823.                                 constantIdx2);
  1824.                         }
  1825.                         else // 3
  1826.                         {
  1827.                             auto constantIdx3 = getOrCreateConstant(new (a)
  1828.                                 ConstantString{ indexStrings[2], compiler });
  1829.  
  1830.                             encodedConstIdx = encodeGlobalConstantIndex(constantIdx1,
  1831.                                 constantIdx2, constantIdx3);
  1832.                         }
  1833.  
  1834.                         auto globalConstIdx =
  1835.                             getOrCreateConstant(new (a) ConstantGlobal{ encodedConstIdx });
  1836.  
  1837.                         if (globalConstIdx < 0x8000)
  1838.                         {
  1839.                             writeInstruction32({ OpCode::GetGlobalConst, reg,
  1840.                                 uint16_t(globalConstIdx) }, indexNameExpr->location.begin);
  1841.  
  1842.                             writeInstruction32(encodedConstIdx,
  1843.                                 indexNameExpr->location.begin);
  1844.                             return;
  1845.                         }
  1846.                     }
  1847.                 }
  1848.  
  1849.                 auto regBackup = regCount;
  1850.  
  1851.                 allocateRegisters(1);
  1852.  
  1853.                 auto idxReg = getLocalOrCompileExpression(indexNameExpr->expr, byte(reg + 1));
  1854.  
  1855.                 auto constantIdx = getOrCreateConstant(new (a)
  1856.                     ConstantString{ indexNameExpr->index.value, compiler });
  1857.  
  1858.                 writeInstruction32({ OpCode::GetTableIndexConstant, reg, idxReg,
  1859.                     byte(hashString(indexNameExpr->index.value)) },
  1860.                     indexNameExpr->indexLocation.begin);
  1861.  
  1862.                 writeInstruction32(constantIdx,
  1863.                     indexNameExpr->indexLocation.begin);
  1864.  
  1865.                 regCount = regBackup;
  1866.             }
  1867.  
  1868.             void compileGlobalExpr(ScriptParser::AstExprGlobal* globalExpr, byte reg)
  1869.             {
  1870.                 auto it = std::find(modifiedGlobals.begin(), modifiedGlobals.end(),
  1871.                     std::string{ globalExpr->name.value });
  1872.  
  1873.                 auto constantIdx = getOrCreateConstant(new (a)
  1874.                     ConstantString{ globalExpr->name.value, compiler });
  1875.                 if (it == modifiedGlobals.end())
  1876.                 {
  1877.                     auto encodedConstIdx = encodeGlobalConstantIndex(constantIdx);
  1878.                     auto globalConstIdx = getOrCreateConstant(new (a)
  1879.                         ConstantGlobal{ encodedConstIdx });
  1880.  
  1881.                     if (constantIdx < 0x400 && globalConstIdx < 0x8000)
  1882.                     {
  1883.                         writeInstruction32({ OpCode::GetGlobalConst, reg,
  1884.                             uint16_t(globalConstIdx) }, globalExpr->location.begin);
  1885.  
  1886.                         writeInstruction32(encodedConstIdx,
  1887.                             globalExpr->location.begin);
  1888.                         return;
  1889.                     }
  1890.                 }
  1891.  
  1892.                 writeInstruction32({ OpCode::GetGlobal,
  1893.                     reg, 0, byte(hashString(globalExpr->name.value)) },
  1894.                     globalExpr->location.begin);
  1895.  
  1896.                 writeInstruction32(constantIdx,
  1897.                     globalExpr->location.begin);
  1898.             }
  1899.  
  1900.             // TODO: rearrange arguments and add defaults
  1901.             byte compileCallExpr(ScriptParser::AstExprCall* callExpr,
  1902.                 byte retBase, byte retCount, bool selfAllocate, bool tailCall)
  1903.             {
  1904.                 auto regBackup = regCount;
  1905.  
  1906.                 byte funcReg;
  1907.                 if (selfAllocate)
  1908.                 {
  1909.                     funcReg = allocateRegisters(1);
  1910.                 }
  1911.                 else
  1912.                 {
  1913.                     funcReg = retBase == regCount - 1 ? retBase : allocateRegisters(1);
  1914.                 }
  1915.  
  1916.                 byte selfIdxReg = funcReg;
  1917.                 ScriptParser::AstExprIndexName* idxNameExpr = nullptr;
  1918.                 if (callExpr->self)
  1919.                 {
  1920.                     allocateRegisters(1);
  1921.                     idxNameExpr = callExpr->func->as<ScriptParser::AstExprIndexName>();
  1922.                     if (idxNameExpr)
  1923.                     {
  1924.                         selfIdxReg = getLocalOrCompileExpression(idxNameExpr->expr, funcReg);
  1925.                     }
  1926.                 }
  1927.  
  1928.                 if (!idxNameExpr)
  1929.                     compileExpression(callExpr->func, funcReg);
  1930.  
  1931.                 // TODO: might cause problems still
  1932.                 // callExpr->self?
  1933.                 // auto extraReg = std::max<int>(callExpr->args.size - retCount, 0);
  1934.                     // std::max(int(callExpr->args.size) - retCount, int(callExpr->self));
  1935.                 // allocateRegisters(extraReg);
  1936.  
  1937.                 auto argBase = regCount;
  1938.  
  1939.                 bool wasTail = false;
  1940.                 for (size_t i = 0; i < callExpr->args.size; ++i)
  1941.                 {
  1942.                     auto expr = callExpr->args.data[i];
  1943.                     if (i + 1 == callExpr->args.size)
  1944.                     {
  1945.                         wasTail = compileTailExpression(expr, allocateRegisters(1));
  1946.                     }
  1947.                     else
  1948.                     {
  1949.                         compileExpression(expr, allocateRegisters(1));
  1950.                     }
  1951.                 }
  1952.  
  1953.                 if (callExpr->self)
  1954.                 {
  1955.                     auto constIdx = getOrCreateConstant(new (a) ConstantString
  1956.                         { idxNameExpr->index.value, compiler });
  1957.  
  1958.                     writeInstruction32({ OpCode::Self, funcReg, selfIdxReg,
  1959.                         byte(hashString(idxNameExpr->index.value)) },
  1960.                         callExpr->location.begin);
  1961.  
  1962.                     writeInstruction32(constIdx, callExpr->location.begin);
  1963.                 }
  1964.  
  1965.                 writeInstruction32({ OpCode::Call, funcReg,
  1966.                     byte(wasTail ? 0 : callExpr->args.size + callExpr->self + 1),
  1967.                     byte(tailCall ? 0 : retCount + 1) },
  1968.                     callExpr->location.begin);
  1969.  
  1970.                 if (!selfAllocate)
  1971.                 {
  1972.                     for (int i = 0; i < retCount; ++i)
  1973.                     {
  1974.                         if (retBase + i == funcReg + i)
  1975.                             continue;
  1976.                         writeInstruction32({ OpCode::Move,
  1977.                             byte(retBase + i), byte(funcReg + i), 0 },
  1978.                             callExpr->location.begin);
  1979.                     }
  1980.                 }
  1981.  
  1982.                 regCount = regBackup;
  1983.  
  1984.                 if (selfAllocate)
  1985.                 {
  1986.                     retBase = allocateRegisters(retCount);
  1987.                 }
  1988.  
  1989.                 return retBase;
  1990.             }
  1991.  
  1992.             void compileRetStat(ScriptParser::AstStatReturn* retStat)
  1993.             {
  1994.                 auto regBackup = regCount;
  1995.  
  1996.                 /*
  1997.                 if (retStat->list.size == 1)
  1998.                 {
  1999.                     auto reg = getLocalOrCompileExpression(retStat->list.data[0], allocateRegisters(1));
  2000.                     writeInstruction32(
  2001.                         { OpCode::Return, reg, 2, 0 },
  2002.                         retStat->location.begin);
  2003.                     return;
  2004.                 }*/
  2005.  
  2006.                 auto retBaseReg = allocateRegisters(retStat->list.size);
  2007.                 bool wasTail = false;
  2008.  
  2009.                 for (size_t i = 0; i < retStat->list.size; ++i)
  2010.                 {
  2011.                     auto expr = retStat->list.data[i];
  2012.                     if (i + 1 == retStat->list.size)
  2013.                     {
  2014.                         wasTail = compileTailExpression(expr, retBaseReg + i);
  2015.                     }
  2016.                     else
  2017.                     {
  2018.                         compileExpression(expr, retBaseReg + i);
  2019.                     }
  2020.                 }
  2021.  
  2022.                 saveUpvalueRegisters(0, retStat->location.begin);
  2023.                 writeInstruction32(
  2024.                     { OpCode::Return, retBaseReg, byte(wasTail ? 0 : retStat->list.size + 1), 0 },
  2025.                     retStat->location.begin);
  2026.  
  2027.                 regCount = regBackup;
  2028.             }
  2029.  
  2030.             void compileStatement(ScriptParser::AstStat* stat)
  2031.             {
  2032.                 if (auto blockStat = stat->as<ScriptParser::AstStatBlock>())
  2033.                 {
  2034.                     const auto localsBackup = locals;
  2035.                     const auto regBackup = regCount;
  2036.  
  2037.                     for (const auto& bodyStat : blockStat->body)
  2038.                     {
  2039.                         compileStatement(bodyStat);
  2040.                     }
  2041.  
  2042.                     saveUpvalueRegisters(regBackup);
  2043.                     regCount = regBackup;
  2044.                     locals = localsBackup;
  2045.                 }
  2046.                 else if (auto ifStat = stat->as<ScriptParser::AstStatIf>())
  2047.                 {
  2048.                     compileIfStat(ifStat);
  2049.                 }
  2050.                 else if (auto whileStat = stat->as<ScriptParser::AstStatWhile>())
  2051.                 {
  2052.                     compileWhileStat(whileStat);
  2053.                 }
  2054.                 else if (auto repeatStat = stat->as<ScriptParser::AstStatRepeat>())
  2055.                 {
  2056.                     compileRepeatStat(repeatStat);
  2057.                 }
  2058.                 else if (auto breakStat = stat->as<ScriptParser::AstStatBreak>())
  2059.                 {
  2060.                     jumpOutQueue.push_back(code.size());
  2061.                     writeInstruction32({ OpCode::Jump, 0, int16_t(0) },
  2062.                         breakStat->location.begin);
  2063.                 }
  2064.                 else if (auto retStat = stat->as<ScriptParser::AstStatReturn>())
  2065.                 {
  2066.                     compileRetStat(retStat);
  2067.                 }
  2068.                 else if (auto exprStat = stat->as<ScriptParser::AstStatExpr>())
  2069.                 {
  2070.                     if (auto callExpr = exprStat->expr->as<ScriptParser::AstExprCall>())
  2071.                     {
  2072.                         compileCallExpr(callExpr, 0, 0, true, false);
  2073.                     }
  2074.                     else
  2075.                     {
  2076.                         throw runtime_error("unexpected error compiling AstStatExpr");
  2077.                     }
  2078.                 }
  2079.                 else if (auto localStat = stat->as<ScriptParser::AstStatLocal>())
  2080.                 {
  2081.                     compileLocalStat(localStat);
  2082.                 }
  2083.                 else if (auto forStat = stat->as<ScriptParser::AstStatFor>())
  2084.                 {
  2085.                     compileForStat(forStat);
  2086.                 }
  2087.                 else if (auto forInStat = stat->as<ScriptParser::AstStatForIn>())
  2088.                 {
  2089.                     compileForInStat(forInStat);
  2090.                 }
  2091.                 else if (auto assignStat = stat->as<ScriptParser::AstStatAssign>())
  2092.                 {
  2093.                     compileAssignStat(assignStat);
  2094.                 }
  2095.             }
  2096.  
  2097.             void compileRepeatStat(ScriptParser::AstStatRepeat* repeatStat)
  2098.             {
  2099.                 auto regBackup = regCount;
  2100.  
  2101.                 auto jumpOutQueueStart = jumpOutQueue.size();
  2102.                 auto loopStartIndex = code.size();
  2103.  
  2104.                 auto localsBackup = locals;
  2105.  
  2106.                 loopDepth++;
  2107.                 compileStatement(repeatStat->body);
  2108.                 loopDepth--;
  2109.  
  2110.                 std::vector<size_t> jumpIndicies;
  2111.                 compileConditionExpr(repeatStat->condition, nullptr, jumpIndicies, true);
  2112.  
  2113.                 auto loopJumpIndex = code.size();
  2114.                 writeInstruction32({ OpCode::LoopJump, 0, int16_t(0) });
  2115.  
  2116.                 locals = localsBackup;
  2117.  
  2118.                 auto loopEndIndex = code.size();
  2119.  
  2120.                 calculateJumps(jumpIndicies, loopEndIndex);
  2121.                 calculateJump(loopJumpIndex, loopStartIndex);
  2122.  
  2123.                 for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
  2124.                 {
  2125.                     calculateJump(jumpOutQueue.at(i), loopEndIndex);
  2126.                 }
  2127.                 jumpOutQueue.resize(jumpOutQueueStart);
  2128.  
  2129.                 regCount = regBackup;
  2130.             }
  2131.  
  2132.             void compileForInStat(ScriptParser::AstStatForIn* forInStat)
  2133.             {
  2134.                 auto jumpOutQueueStart = jumpOutQueue.size();
  2135.                 auto regBackup = regCount;
  2136.  
  2137.                 auto assignBase = allocateRegisters(3);
  2138.                 bool wasTail = false;
  2139.                 for (size_t i = 0; i < forInStat->values.size; ++i)
  2140.                 {
  2141.                     auto expr = forInStat->values.data[i];
  2142.                     if (i + 1 == forInStat->values.size)
  2143.                     {
  2144.                         if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
  2145.                         {
  2146.                             compileCallExpr(callExpr, assignBase + i,
  2147.                                 3 - i, false, false);
  2148.                             wasTail = true;
  2149.                         }
  2150.                         else if (expr->as<ScriptParser::AstExprVarargs>())
  2151.                         {
  2152.                             writeInstruction32({ OpCode::LoadVarargs,
  2153.                                 byte(3 - i + 1), 0, 0 },
  2154.                                 expr->location.begin);
  2155.                             wasTail = true;
  2156.                         }
  2157.                         else
  2158.                         {
  2159.                             compileExpression(expr, assignBase + i);
  2160.                         }
  2161.                     }
  2162.                     else
  2163.                     {
  2164.                         compileExpression(expr, assignBase + i);
  2165.                     }
  2166.                 }
  2167.  
  2168.                 if (!wasTail
  2169.                     && forInStat->values.size < 3)
  2170.                 {
  2171.                     for (auto i = assignBase + forInStat->values.size; i <= assignBase + forInStat->values.size
  2172.                         + (3 - forInStat->values.size - 1); ++i)
  2173.                     {
  2174.                         writeInstruction32({ OpCode::LoadNil, byte(i), 0, 0 },
  2175.                             forInStat->location.begin);
  2176.                     }
  2177.                 }
  2178.  
  2179.                 auto localsBackup = locals;
  2180.  
  2181.                 auto varBase = allocateRegisters(2);
  2182.                 for (size_t i = 0; i < forInStat->vars.size; ++i)
  2183.                 {
  2184.                     assignLocal(varBase + i, forInStat->vars.data[i]->name.value);
  2185.                 }
  2186.  
  2187.                 auto loopStartIndex = code.size();
  2188.                 writeInstruction32({ OpCode::Jump, assignBase, int16_t(0) },
  2189.                     forInStat->location.begin);
  2190.  
  2191.                 auto bodyStartIndex = code.size();
  2192.                 loopDepth++;
  2193.                 compileStatement(forInStat->body);
  2194.                 loopDepth--;
  2195.                 saveUpvalueRegisters(regBackup);
  2196.  
  2197.                 locals = localsBackup;
  2198.  
  2199.                 auto tForLoopIndex = code.size();
  2200.                 writeInstruction32({ OpCode::TForLoop, assignBase, int16_t(0) });
  2201.                 writeInstruction32({ forInStat->vars.size });
  2202.                 auto loopEndIndex = code.size();
  2203.  
  2204.                 calculateJump(loopStartIndex, tForLoopIndex);
  2205.                 calculateJump(tForLoopIndex, bodyStartIndex);
  2206.  
  2207.                 for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
  2208.                 {
  2209.                     calculateJump(jumpOutQueue.at(i), loopEndIndex);
  2210.                 }
  2211.                 jumpOutQueue.resize(jumpOutQueueStart);
  2212.  
  2213.                 regCount = regBackup;
  2214.             }
  2215.  
  2216.             void compileForStat(ScriptParser::AstStatFor* forStat)
  2217.             {
  2218.                 auto jumpOutQueueStart = jumpOutQueue.size();
  2219.                 auto regBackup = regCount;
  2220.                 auto localsBackup = locals;
  2221.  
  2222.                 auto prepArgBase = allocateRegisters(3);
  2223.  
  2224.                 assignLocal(prepArgBase + 2, forStat->var->name.value);
  2225.  
  2226.                 compileExpression(forStat->from, prepArgBase + 2);
  2227.                 compileExpression(forStat->to, prepArgBase);
  2228.                 if (forStat->step)
  2229.                 {
  2230.                     compileExpression(forStat->step, prepArgBase + 1);
  2231.                 }
  2232.                 else
  2233.                 {
  2234.                     writeInstruction32({ OpCode::LoadShort, byte(prepArgBase + 1), 1, 0 },
  2235.                         forStat->to->location.end);
  2236.                 }
  2237.  
  2238.                 auto forPrepIndex = code.size();
  2239.                 writeInstruction32({ OpCode::ForPrep, prepArgBase, int16_t(0) },
  2240.                     forStat->to->location.begin);
  2241.  
  2242.                 auto bodyStartIndex = code.size();
  2243.                 loopDepth++;
  2244.                 compileStatement(forStat->body);
  2245.                 loopDepth--;
  2246.                 saveUpvalueRegisters(regBackup);
  2247.  
  2248.                 auto forLoopIndex = code.size();
  2249.                 writeInstruction32({ OpCode::ForLoop, prepArgBase, int16_t(0) },
  2250.                     forStat->body->location.begin);
  2251.  
  2252.                 auto loopEndIndex = code.size();
  2253.  
  2254.                 calculateJump(forPrepIndex, loopEndIndex);
  2255.                 calculateJump(forLoopIndex, bodyStartIndex);
  2256.  
  2257.  
  2258.                 for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
  2259.                 {
  2260.                     calculateJump(jumpOutQueue.at(i), loopEndIndex);
  2261.                 }
  2262.                 jumpOutQueue.resize(jumpOutQueueStart);
  2263.  
  2264.                 regCount = regBackup;
  2265.                 locals = localsBackup;
  2266.             }
  2267.  
  2268.             void compileLocalStat(ScriptParser::AstStatLocal* localStat)
  2269.             {
  2270.                 auto varBase = allocateRegisters(localStat->vars.size);
  2271.  
  2272.                 if (localStat->usingFunctionSugar)
  2273.                 {
  2274.                     for (size_t i = 0; i < localStat->vars.size; ++i)
  2275.                     {
  2276.                         std::string localName = localStat->vars.data[i]->name.value;
  2277.                         assignLocal(varBase + i, localName);
  2278.                     }
  2279.                 }
  2280.  
  2281.                 bool wasTail = false;
  2282.                 for (size_t i = 0; i < localStat->values.size; ++i)
  2283.                 {
  2284.                     auto expr = localStat->values.data[i];
  2285.                     if (i + 1 == localStat->values.size)
  2286.                     {
  2287.                         if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
  2288.                         {
  2289.                             compileCallExpr(callExpr, varBase + i,
  2290.                                 localStat->vars.size - i, false, false);
  2291.                             wasTail = true;
  2292.                         }
  2293.                         else if (expr->as<ScriptParser::AstExprVarargs>())
  2294.                         {
  2295.                             writeInstruction32({ OpCode::LoadVarargs,
  2296.                                 byte(localStat->vars.size - i + 1), 0, 0 },
  2297.                                 expr->location.begin);
  2298.                             wasTail = true;
  2299.                         }
  2300.                         else
  2301.                         {
  2302.                             compileExpression(expr, varBase + i);
  2303.                         }
  2304.                     }
  2305.                     else
  2306.                     {
  2307.                         compileExpression(expr, varBase + i);
  2308.                     }
  2309.                 }
  2310.  
  2311.                 if (!localStat->usingFunctionSugar)
  2312.                 {
  2313.                     for (size_t i = 0; i < localStat->vars.size; ++i)
  2314.                     {
  2315.                         std::string localName = localStat->vars.data[i]->name.value;
  2316.                         assignLocal(varBase + i, localName);
  2317.                     }
  2318.                 }
  2319.  
  2320.                 if (!wasTail
  2321.                     && localStat->vars.size != localStat->values.size)
  2322.                 {
  2323.                     for (auto i = varBase + localStat->values.size; i <= varBase + localStat->values.size
  2324.                         + (localStat->vars.size - localStat->values.size - 1); ++i)
  2325.                     {
  2326.                         writeInstruction32({ OpCode::LoadNil, byte(i), 0, 0 },
  2327.                             localStat->location.begin);
  2328.                     }
  2329.                 }
  2330.             }
  2331.  
  2332.             // TODO: remove extra move on locals
  2333.             void compileAssignStat(ScriptParser::AstStatAssign* assignStat)
  2334.             {
  2335.                 /*
  2336.                 if (assignStat->vars.size == 1
  2337.                     && assignStat->values.size == 1)
  2338.                 {
  2339.                     auto assignBase = allocateRegisters(1);
  2340.                     compileExpression(assignStat->values.data[0], assignBase);
  2341.                     compileAssignment(assignStat->vars.data[0], assignBase);
  2342.                     freeRegisters(1);
  2343.                     return;
  2344.                 }*/
  2345.  
  2346.                 auto regBackup = regCount;
  2347.  
  2348.                 std::unordered_map<ScriptParser::AstExprIndexName*, byte> indexMap;
  2349.  
  2350.                 for (size_t i = 0; i < assignStat->vars.size; ++i)
  2351.                 {
  2352.                     auto expr = assignStat->vars.data[i];
  2353.                     if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
  2354.                     {
  2355.                         byte reg = allocateRegisters(1);
  2356.                         indexMap[indexNameExpr] = reg;
  2357.                         compileExpression(indexNameExpr->expr, reg);
  2358.                     }
  2359.                 }
  2360.  
  2361.                 auto assignBase = allocateRegisters(assignStat->vars.size);
  2362.  
  2363.                 bool wasTail = false;
  2364.                 for (size_t i = 0; i < assignStat->values.size; ++i)
  2365.                 {
  2366.                     auto expr = assignStat->values.data[i];
  2367.                     if (i + 1 == assignStat->values.size)
  2368.                     {
  2369.                         if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
  2370.                         {
  2371.                             compileCallExpr(callExpr, assignBase + i,
  2372.                                 assignStat->vars.size - i, false, false);
  2373.                             wasTail = true;
  2374.                         }
  2375.                         else if (expr->as<ScriptParser::AstExprVarargs>())
  2376.                         {
  2377.                             writeInstruction32({ OpCode::LoadVarargs,
  2378.                                                    byte(assignStat->vars.size - i + 1), 0, 0 },
  2379.                                 expr->location.begin);
  2380.                             wasTail = true;
  2381.                         }
  2382.                         else
  2383.                         {
  2384.                             compileExpression(expr, assignBase + i);
  2385.                         }
  2386.                     }
  2387.                     else
  2388.                     {
  2389.                         compileExpression(expr, assignBase + i);
  2390.                     }
  2391.                 }
  2392.  
  2393.                 if (!wasTail
  2394.                     && assignStat->vars.size != assignStat->values.size)
  2395.                 {
  2396.                     for (auto i = assignBase + assignStat->values.size; i <= assignBase + assignStat->values.size
  2397.                         + (assignStat->vars.size - assignStat->values.size - 1); ++i)
  2398.                     {
  2399.                         writeInstruction32({ OpCode::LoadNil, byte(i), 0, 0 },
  2400.                             assignStat->location.begin);
  2401.                     }
  2402.                 }
  2403.  
  2404.                 for (size_t i = 0; i < assignStat->vars.size; ++i)
  2405.                 {
  2406.                     auto expr = assignStat->vars.data[i];
  2407.  
  2408.                     if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
  2409.                     {
  2410.                         auto idxName = indexNameExpr->index.value;
  2411.  
  2412.                         writeInstruction32({ OpCode::SetTableIndexConstant, byte(assignBase + i),
  2413.                             indexMap.at(indexNameExpr), byte(hashString(idxName)) });
  2414.  
  2415.                         writeInstruction32(getOrCreateConstant(new (a)
  2416.                             ConstantString{ idxName, compiler }),
  2417.                             indexNameExpr->indexLocation.begin);
  2418.                     }
  2419.                     else
  2420.                     {
  2421.                         compileAssignment(expr, assignBase + i);
  2422.                     }
  2423.                 }
  2424.  
  2425.                 regCount = regBackup;
  2426.             }
  2427.  
  2428.             void compileAssignment(ScriptParser::AstExpr* var, byte reg)
  2429.             {
  2430.                 if (auto localExpr = var->as<ScriptParser::AstExprLocal>())
  2431.                 {
  2432.                     compileLocalExpr(localExpr, reg, true);
  2433.                 }
  2434.                 else if (auto globalExpr = var->as<ScriptParser::AstExprGlobal>())
  2435.                 {
  2436.                     writeInstruction32({ OpCode::SetGlobal,
  2437.                         reg, 0, byte(hashString(globalExpr->name.value)) },
  2438.                         globalExpr->location.begin);
  2439.  
  2440.                     auto constantIdx = getOrCreateConstant(new (a)
  2441.                         ConstantString{ globalExpr->name.value, compiler });
  2442.  
  2443.                     writeInstruction32(constantIdx, globalExpr->location.begin);
  2444.                     modifiedGlobals.emplace_back(globalExpr->name.value);
  2445.                 }
  2446.                 else if (auto indexNameExpr = var->as<ScriptParser::AstExprIndexName>())
  2447.                 {
  2448.                     auto exprIdxReg = allocateRegisters(1);
  2449.  
  2450.                     compileExpressionIndexOptimized(indexNameExpr->expr, exprIdxReg);
  2451.  
  2452.                     auto idxExpr = indexNameExpr->index;
  2453.                     writeInstruction32({ OpCode::SetTableIndexConstant, reg, exprIdxReg,
  2454.                         byte(hashString(idxExpr.value)) },
  2455.                         indexNameExpr->indexLocation.begin);
  2456.  
  2457.                     writeInstruction32(getOrCreateConstant(new (a)
  2458.                         ConstantString{ idxExpr.value, compiler }),
  2459.                         indexNameExpr->indexLocation.begin);
  2460.  
  2461.                     freeRegisters(1);
  2462.                 }
  2463.                 else if (auto indexExpr = var->as<ScriptParser::AstExprIndexExpr>())
  2464.                 {
  2465.                     auto exprIdxBase = allocateRegisters(1);
  2466.  
  2467.                     compileExpressionIndexOptimized(indexExpr->expr, exprIdxBase);
  2468.  
  2469.                     auto numConstExpr = indexExpr->index->as<ScriptParser::AstExprConstantNumber>();
  2470.                     if (numConstExpr
  2471.                         && floor(numConstExpr->value) == numConstExpr->value
  2472.                         && numConstExpr->value >= 1
  2473.                         && numConstExpr->value <= UCHAR_MAX + 1)
  2474.                     {
  2475.                         writeInstruction32({ OpCode::SetTableIndexByte, reg, exprIdxBase,
  2476.                             byte(numConstExpr->value - 1) }, numConstExpr->location.begin);
  2477.                     }
  2478.                     else
  2479.                     {
  2480.                         auto indexValue = allocateRegisters(1);
  2481.  
  2482.                         compileExpression(indexExpr->index, indexValue);
  2483.  
  2484.                         writeInstruction32({ OpCode::SetTableIndex, reg, exprIdxBase, indexValue },
  2485.                             indexExpr->index->location.begin);
  2486.  
  2487.                         freeRegisters(1);
  2488.                     }
  2489.  
  2490.                     freeRegisters(1);
  2491.                 }
  2492.             }
  2493.  
  2494.             void saveUpvalueRegisters(int base, const ScriptParser::Position& pos)
  2495.             {
  2496.                 byte last_reg = 255;
  2497.                 for (auto it = passedUpvals.begin(); it != passedUpvals.end();)
  2498.                 {
  2499.                     if (it->second >= base
  2500.                         && it->second < last_reg)
  2501.                     {
  2502.                         last_reg = it->second;
  2503.                         it = passedUpvals.erase(it);
  2504.                         writeInstruction32({ OpCode::SaveRegisters, last_reg, 0, 0 }, pos);
  2505.                     }
  2506.                     else
  2507.                     {
  2508.                         it++;
  2509.                     }
  2510.                 }
  2511.             }
  2512.  
  2513.             void saveUpvalueRegisters(int base)
  2514.             {
  2515.                 byte last_reg = 255;
  2516.                 for (auto it = passedUpvals.begin(); it != passedUpvals.end();)
  2517.                 {
  2518.                     if (it->second >= base
  2519.                         && it->second < last_reg)
  2520.                     {
  2521.                         last_reg = it->second;
  2522.                         it = passedUpvals.erase(it);
  2523.                         writeInstruction32({ OpCode::SaveRegisters, last_reg, 0, 0 });
  2524.                     }
  2525.                     else
  2526.                     {
  2527.                         it++;
  2528.                     }
  2529.                 }
  2530.             }
  2531.  
  2532.             void compileFunction(ScriptParser::AstExprFunction* funcExpr, bool isMain = false)
  2533.             {
  2534.                 bool hasSelf = funcExpr->self != nullptr;
  2535.  
  2536.                 isVarArg = funcExpr->vararg;
  2537.                 argCount = byte(hasSelf + funcExpr->args.size);
  2538.  
  2539.                 writeInstruction32({ OpCode(byte(OpCode::ClearStack) + funcExpr->vararg),
  2540.                     byte(hasSelf + funcExpr->args.size), 0, 0 },
  2541.                     funcExpr->location.begin);
  2542.  
  2543.                 const byte argBaseIndex = allocateRegisters(hasSelf + funcExpr->args.size);
  2544.  
  2545.                 if (hasSelf)
  2546.                 {
  2547.                     assignLocal(argBaseIndex, funcExpr->self->name.value);
  2548.                 }
  2549.  
  2550.                 for (size_t i = 0; i < funcExpr->args.size; ++i)
  2551.                 {
  2552.                     const auto local = funcExpr->args.data[i];
  2553.  
  2554.                     assignLocal(argBaseIndex + hasSelf + i, local->name.value);
  2555.                 }
  2556.  
  2557.                 if (auto block = funcExpr->body->as<ScriptParser::AstStatBlock>())
  2558.                 {
  2559.                     for (const auto& stat : block->body)
  2560.                     {
  2561.                         compileStatement(stat);
  2562.                     }
  2563.  
  2564.                     if (!ParserUtils::statReturns(block))
  2565.                     {
  2566.                         saveUpvalueRegisters(0, block->location.end);
  2567.                         writeInstruction32({ OpCode::Return, 0, 1, 0 },
  2568.                             funcExpr->location.end);
  2569.                     }
  2570.                 }
  2571.             }
  2572.         };
  2573.  
  2574.         tsl::ordered_map<ScriptParser::AstExprFunction*, FunctionPrototype> protos;
  2575.     public:
  2576.         Compiler(bool debugInfo = false)
  2577.             : debugInfo(debugInfo) {}
  2578.  
  2579.         void compile(ScriptParser::AstStat* root, ScriptParser::AstNameTable& names,
  2580.             ScriptParser::Allocator& a)
  2581.         {
  2582.             root->visit(&assignmentVisitor);
  2583.             root->visit(&functionVisitor);
  2584.  
  2585.             for (auto funcExpr : funcExprs)
  2586.             {
  2587.                 FunctionPrototype p{ a, *this };
  2588.  
  2589.                 if (funcExpr.second)
  2590.                 {
  2591.                     funcExpr.second->visit(&constantVisitor);
  2592.  
  2593.                     if (auto localStat = funcExpr.second->as<ScriptParser::AstStatLocal>())
  2594.                     {
  2595.                         p.name = localStat->vars.data[0]->name.value;
  2596.                         auto nameIt = std::find(strings.cbegin(),
  2597.                             strings.cend(), p.name.value());
  2598.  
  2599.                         if (nameIt == strings.cend())
  2600.                         {
  2601.                             strings.push_back(p.name.value());
  2602.                         }
  2603.                     }
  2604.                     else if (auto assignStat =
  2605.                         funcExpr.second->as<ScriptParser::AstStatAssign>())
  2606.                     {
  2607.                         auto global =
  2608.                             assignStat->vars.data[0]->as<ScriptParser::AstExprGlobal>();
  2609.  
  2610.                         if (global)
  2611.                         {
  2612.                             p.name = global->name.value;
  2613.                         }
  2614.                     }
  2615.                 }
  2616.                 else
  2617.                 {
  2618.                     funcExpr.first->visit(&constantVisitor);
  2619.                 }
  2620.  
  2621.                 p.compileFunction(funcExpr.first);
  2622.                 protos.insert({ funcExpr.first, p });
  2623.             }
  2624.  
  2625.             if (auto block = root->as<ScriptParser::AstStatBlock>())
  2626.             {
  2627.                 auto funcExpr = new (a) ScriptParser::AstExprFunction{
  2628.                     block->location, nullptr, {}, true, block
  2629.                 };
  2630.  
  2631.                 FunctionPrototype p{ a, *this };
  2632.                 p.compileFunction(funcExpr, true);
  2633.  
  2634.                 protos.insert({ funcExpr, p });
  2635.             }
  2636.         }
  2637.  
  2638.         void serialize(ByteStream& buffer)
  2639.         {
  2640.             buffer << true; // successful compilation
  2641.  
  2642.             buffer << int(strings.size());
  2643.             for (const auto& str : strings)
  2644.             {
  2645.                 buffer << int(str.size());
  2646.                 buffer << str;
  2647.             }
  2648.  
  2649.             buffer << int(protos.size());
  2650.             for (const auto& protoEntry : protos)
  2651.             {
  2652.                 auto& proto = protoEntry.second;
  2653.  
  2654.                 buffer << proto.maxRegCount
  2655.                     << proto.argCount
  2656.                     << byte(proto.upvals.size())
  2657.                     << proto.isVarArg;
  2658.  
  2659.                 buffer << int(proto.code.size());
  2660.  
  2661. #ifdef _DEBUG
  2662.                 {
  2663.                     for (auto it = proto.code.begin(); it != proto.code.end(); ++it)
  2664.                     {
  2665.                         uint32_t instrData = *it;
  2666.                         auto instr = Instruction32{ instrData };
  2667.  
  2668.                         bool wideInstruction = false;
  2669.  
  2670.                         switch (instr.op)
  2671.                         {
  2672.                         case OpCode::GetGlobal:
  2673.                         case OpCode::SetGlobal:
  2674.                         case OpCode::GetGlobalConst:
  2675.                         case OpCode::GetTableIndexConstant:
  2676.                         case OpCode::SetTableIndexConstant:
  2677.                         case OpCode::Self:
  2678.                         case OpCode::Equal:
  2679.                         case OpCode::LesserOrEqual:
  2680.                         case OpCode::LesserThan:
  2681.                         case OpCode::NotEqual:
  2682.                         case OpCode::GreaterThan:
  2683.                         case OpCode::GreaterOrEqual:
  2684.                         case OpCode::NewTable:
  2685.                         case OpCode::SetList:
  2686.                         case OpCode::TForLoop:
  2687.                         case OpCode::LoadConstLarge:
  2688.                             wideInstruction = true;
  2689.                         default:;
  2690.                         }
  2691.  
  2692.                         {
  2693.                             auto encodedBytes = (byte*)&instr.encoded;
  2694.  
  2695.                             for (int i = 0; i < 4; ++i)
  2696.                             {
  2697.                                 std::cout << int(encodedBytes[i]) << " ";
  2698.                             }
  2699.                         }
  2700.  
  2701.                         byte clientOp = getClientOp(instr.op);
  2702.  
  2703.                         instr.op = OpCode(clientOp);
  2704.  
  2705.                         buffer << instr.encoded;
  2706.  
  2707.                         if (wideInstruction)
  2708.                         {
  2709.                             it++;
  2710.  
  2711.                             auto encodedBytes = (byte*)&*it;
  2712.  
  2713.                             for (int i = 0; i < 4; ++i)
  2714.                             {
  2715.                                 std::cout << int(encodedBytes[i]) << " ";
  2716.                             }
  2717.  
  2718.                             buffer << *it;
  2719.                         }
  2720.                         else
  2721.                         {
  2722.                             std::cout << "\t";
  2723.                         }
  2724.  
  2725.  
  2726.                         std::cout << "\t; client: " << int(clientOp) << "\n";
  2727.                     }
  2728.                 }
  2729.                 std::cout << "\n";
  2730. #else
  2731.                 auto& dataVec = buffer.vec();
  2732.                 auto initialDataVecSize = dataVec.size();
  2733.                 auto codeLenBytes = sizeof(uint32_t) * proto.code.size();
  2734.  
  2735.                 dataVec.resize(dataVec.size() + codeLenBytes);
  2736.                 memcpy(&dataVec[initialDataVecSize],
  2737.                     &proto.code[0], codeLenBytes);
  2738. #endif
  2739.  
  2740.                 buffer << int(proto.constants.size());
  2741.                 for (const auto& constant : proto.constants)
  2742.                 {
  2743.                     buffer << byte(constant->getType());
  2744.                     constant->writeData(buffer);
  2745.                 }
  2746.  
  2747.                 buffer << int(proto.closures.size());
  2748.                 for (const auto& funcExpr : proto.closures)
  2749.                 {
  2750.                     buffer << int(std::distance(protos.begin(), protos.find(funcExpr)));
  2751.                 }
  2752.  
  2753.                 // function name index
  2754.                 if (proto.name.has_value())
  2755.                 {
  2756.                     auto nameString = proto.name.value();
  2757.                     auto nameIt =
  2758.                         std::find(strings.cbegin(), strings.cend(), nameString);
  2759.                     if (nameIt != strings.cend())
  2760.                     {
  2761.                         auto nameIndex = std::distance(strings.cbegin(), nameIt);
  2762.                         buffer << int(nameIndex + 1);
  2763.                     }
  2764.                     else
  2765.                     {
  2766.                         buffer << int(0);
  2767.                     }
  2768.                 }
  2769.                 else
  2770.                 {
  2771.                     buffer << int(0);
  2772.                 }
  2773.  
  2774.                 buffer << int(proto.lineInfo.size());
  2775.  
  2776.                 for (const auto& line : proto.lineInfo)
  2777.                 {
  2778.                     if (line > 0 && line < SCHAR_MAX)
  2779.                     {
  2780.                         buffer << byte(line);
  2781.                     }
  2782.                     else
  2783.                     {
  2784.                         buffer << int(line);
  2785.                     }
  2786.                 }
  2787.  
  2788.                 buffer << debugInfo;
  2789.                 if (debugInfo)
  2790.                 {
  2791.                     throw runtime_error("debug info is not supported");
  2792.                 }
  2793.             }
  2794.  
  2795.             buffer << int(protos.size() - 1); // main proto index
  2796.         }
  2797.     };
  2798.  
  2799.     void compile(ByteStream& buffer, const std::string& source, bool debugInfo)
  2800.     {
  2801. #ifndef _DEBUG
  2802.         try
  2803. #endif
  2804.         {
  2805.             ScriptParser::Allocator a;
  2806.             ScriptParser::AstNameTable names{ a };
  2807.             ScriptParser::AstStat* root =
  2808.                 ScriptParser::parse(source.data(), source.size(), names, a);
  2809.  
  2810.             Compiler c{ debugInfo };
  2811.             c.compile(root, names, a);
  2812.  
  2813.             c.serialize(buffer);
  2814.         }
  2815. #ifndef _DEBUG
  2816.         catch (ScriptParser::ParseError& e)
  2817.         {
  2818.             buffer << false // unsuccessful compilation
  2819.                 << (std::stringstream{} << ":" << e.getLocation().begin.line << ": " << e.what()).str();
  2820.         }
  2821.         catch (std::exception& e)
  2822.         {
  2823.             buffer.clear(); // incase the exception was encountered mid-serialization
  2824.             buffer << false // unsuccessful compilation
  2825.                 << ":" << std::string(e.what());
  2826.         }
  2827. #endif
  2828.     }
  2829. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement