Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "Compiler.h"
- #include "ScriptParser.h"
- #include <sstream>
- #include "parallel_hashmap/phmap.h"
- #include <iostream>
- #include "ordered_map.h"
- using namespace RBX;
- #define MAX_REG_COUNT UCHAR_MAX
- #define MAX_LOCAL_COUNT UCHAR_MAX
- template <typename ResultT, ResultT OffsetBasis, ResultT Prime>
- class basic_fnv1a final
- {
- static_assert(std::is_unsigned<ResultT>::value, "need unsigned integer");
- public:
- using result_type = ResultT;
- private:
- result_type state_{};
- public:
- constexpr
- basic_fnv1a() noexcept : state_{ OffsetBasis }
- {
- }
- constexpr void
- update(const void *const data, const std::size_t size) noexcept
- {
- const auto cdata = static_cast<const unsigned char *>(data);
- auto acc = this->state_;
- for (auto i = std::size_t{}; i < size; ++i)
- {
- const auto next = std::size_t{ cdata[i] };
- acc = (acc ^ next) * Prime;
- }
- this->state_ = acc;
- }
- constexpr result_type
- digest() const noexcept
- {
- return this->state_;
- }
- };
- namespace Luau
- {
- namespace ParserUtils
- {
- enum class RecurseDirection : bool
- {
- Left,
- Right
- };
- size_t getBinaryExprDepth(ScriptParser::AstExpr* expr,
- ScriptParser::AstExprBinary::Op op, RecurseDirection direction, size_t c = 0)
- {
- ++c;
- auto binaryExpr = expr->as<ScriptParser::AstExprBinary>();
- if (binaryExpr && binaryExpr->op == op)
- {
- return getBinaryExprDepth(
- direction == RecurseDirection::Right ? binaryExpr->right : binaryExpr->left,
- op, direction, c);
- }
- return c;
- }
- size_t getIndexNameExprDepth(ScriptParser::AstExpr* expr, size_t c = 0)
- {
- ++c;
- if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
- {
- return getIndexNameExprDepth(indexNameExpr->expr, c);
- }
- return c;
- }
- bool exprContainsGlobal(ScriptParser::AstExpr* expr)
- {
- if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
- {
- return exprContainsGlobal(indexNameExpr->expr);
- }
- return expr->is<ScriptParser::AstExprGlobal>();
- }
- bool statBreaks(ScriptParser::AstStat* stat)
- {
- if (auto blockStat = stat->as<ScriptParser::AstStatBlock>())
- {
- return blockStat->body.size == 1
- && blockStat->body.data[0]->is<ScriptParser::AstStatBreak>();
- }
- return stat->is<ScriptParser::AstStatBreak>();
- }
- bool statReturns(ScriptParser::AstStat* stat)
- {
- if (auto block = stat->as<ScriptParser::AstStatBlock>())
- {
- return block->body.size ? statReturns(block->body.data[block->body.size - 1]) : false;
- }
- return stat->is<ScriptParser::AstStatReturn>();
- }
- }
- class Compiler
- {
- bool debugInfo;
- std::vector<std::string> strings;
- std::vector<std::pair<ScriptParser::AstExprFunction*,
- ScriptParser::AstStat*>> funcExprs;
- struct AssignmentVisitor : ScriptParser::AstVisitor
- {
- } assignmentVisitor{};
- // collects constant values
- class ConstantVisitor : public ScriptParser::AstVisitor
- {
- Compiler& c;
- void addStringEntry(const std::string& str)
- {
- auto it = std::find(c.strings.begin(), c.strings.end(), str);
- if (it == c.strings.end())
- {
- c.strings.push_back(str);
- }
- }
- public:
- ConstantVisitor(Compiler& c) : c(c) {}
- bool visit(ScriptParser::AstExprConstantString* stringExpr) override
- {
- addStringEntry({ stringExpr->value.data, stringExpr->value.size });
- return false;
- }
- bool visit(ScriptParser::AstExprGlobal* globalExpr) override
- {
- addStringEntry({ globalExpr->name.value });
- return false;
- }
- bool visit(ScriptParser::AstExprIndexName* indexNameExpr) override
- {
- indexNameExpr->expr->visit(this);
- addStringEntry({ indexNameExpr->index.value });
- return false;
- }
- bool visit(ScriptParser::AstExprCall* callExpr) override
- {
- ScriptParser::AstExprIndexName* indexNameExpr = nullptr;
- if (callExpr->self)
- {
- if ((indexNameExpr =
- callExpr->func->as<ScriptParser::AstExprIndexName>()))
- {
- indexNameExpr->expr->visit(this);
- }
- else
- {
- callExpr->func->visit(this);
- }
- }
- else
- {
- callExpr->func->visit(this);
- }
- for (auto a : callExpr->args)
- {
- a->visit(this);
- }
- if (indexNameExpr)
- {
- addStringEntry({ indexNameExpr->index.value });
- }
- return false;
- }
- bool visit(ScriptParser::AstStatRepeat* repeatStat)
- {
- repeatStat->body->visit(this);
- repeatStat->condition->visit(this);
- return false;
- }
- // TODO: move to assignment visitor? check roblox studio
- // TODO: unsure whether this is right. should test further
- bool visit(ScriptParser::AstStatAssign* assignStat) override
- {
- for (auto val : assignStat->values)
- {
- val->visit(this);
- }
- for (auto var : assignStat->vars)
- {
- var->visit(this);
- }
- return false;
- }
- } constantVisitor{ *this };
- // collects function prototypes
- class FunctionVisitor : public ScriptParser::AstVisitor
- {
- Compiler& c;
- public:
- FunctionVisitor(Compiler& c) : c(c) {}
- bool visit(ScriptParser::AstStatLocal* localStat) override
- {
- if (localStat->usingFunctionSugar)
- {
- auto funcExpr =
- localStat->values.data[0]->as<ScriptParser::AstExprFunction>();
- if (funcExpr)
- {
- funcExpr->body->visit(this);
- c.funcExprs.emplace_back(funcExpr, localStat);
- return false;
- }
- }
- for (auto val : localStat->values)
- {
- val->visit(this);
- }
- return false;
- }
- bool visit(ScriptParser::AstStatAssign* assignStat) override
- {
- if (assignStat->usingFunctionSugar)
- {
- auto funcExpr =
- assignStat->values.data[0]->as<ScriptParser::AstExprFunction>();
- if (funcExpr)
- {
- funcExpr->body->visit(this);
- c.funcExprs.emplace_back(funcExpr, assignStat);
- return false;
- }
- }
- for (auto val : assignStat->values)
- {
- val->visit(this);
- }
- return false;
- }
- bool visit(ScriptParser::AstExprFunction* funcExpr) override
- {
- funcExpr->body->visit(this);
- c.funcExprs.emplace_back(funcExpr, nullptr);
- return false;
- }
- } functionVisitor{ *this };
- enum class OpCode : byte
- {
- Nop,
- SaveCode,
- LoadNil,
- LoadBool, // A B C R(A) := (Bool)B; pc += C
- LoadShort,
- LoadConst,
- Move,
- GetGlobal,
- SetGlobal,
- GetUpvalue,
- SetUpvalue,
- SaveRegisters,
- GetGlobalConst,
- GetTableIndex,
- SetTableIndex,
- GetTableIndexConstant,
- SetTableIndexConstant,
- GetTableIndexByte,
- SetTableIndexByte,
- Closure,
- Self,
- Call,
- Return,
- Jump,
- LoopJump,
- Test,
- NotTest,
- Equal,
- LesserOrEqual,
- LesserThan,
- NotEqual,
- GreaterThan,
- GreaterOrEqual,
- Add,
- Sub,
- Mul,
- Div,
- Mod,
- Pow,
- AddByte,
- SubByte,
- MulByte,
- DivByte,
- ModByte,
- PowByte,
- Or,
- And,
- OrByte,
- AndByte,
- Concat,
- Not,
- UnaryMinus,
- Len,
- NewTable,
- NewTableConst,
- SetList,
- ForPrep,
- ForLoop,
- TForLoop,
- LoopJumpIPairs,
- TForLoopIPairs,
- LoopJumpNext,
- TForLoopNext,
- LoadVarargs,
- ClearStack,
- ClearStackFull,
- LoadConstLarge,
- FarJump,
- BuiltinCall
- };
- static constexpr byte getClientOp(OpCode op)
- {
- return 227 - (29 * (byte(op) - 1));
- }
- struct Instruction32
- {
- union
- {
- uint32_t encoded = 0;
- struct
- {
- OpCode op;
- byte a;
- union
- {
- struct
- {
- byte b;
- byte c;
- };
- uint16_t b_x;
- int16_t s_b_x;
- };
- };
- };
- bool extraData = false;
- Instruction32(uint32_t encoded)
- : encoded(encoded), extraData(true) {}
- Instruction32(OpCode op, byte a, byte b, byte c)
- : op(op), a(a), b(b), c(c) {}
- Instruction32(OpCode op, byte a, uint16_t b_x)
- : op(op), a(a), b_x(b_x) {}
- Instruction32(OpCode op, byte a, int16_t s_b_x)
- : op(op), a(a), s_b_x(s_b_x) {}
- };
- enum class ConstantType : byte
- {
- ConstantNil,
- ConstantBoolean,
- ConstantNumber,
- ConstantString,
- ConstantGlobal,
- ConstantHashTable
- };
- struct Constant
- {
- virtual ~Constant() = default;
- virtual ConstantType getType() = 0;
- virtual void writeData(ByteStream& buffer) = 0;
- virtual bool equals(Constant* rhs) = 0;
- };
- struct ConstantNil : Constant
- {
- ConstantType getType() override
- {
- return ConstantType::ConstantNil;
- }
- void writeData(ByteStream& buffer) override {}
- bool equals(Constant* rhs) override
- {
- return true;
- }
- };
- struct ConstantBoolean : Constant
- {
- bool value;
- ConstantBoolean(bool value) : value(value) {}
- ConstantType getType() override
- {
- return ConstantType::ConstantBoolean;
- }
- void writeData(ByteStream& buffer) override
- {
- buffer << value;
- }
- bool equals(Constant* rhs) override
- {
- if (auto other = dynamic_cast<ConstantBoolean*>(rhs))
- {
- return value == other->value;
- }
- return false;
- }
- };
- struct ConstantNumber : Constant
- {
- double value;
- ConstantNumber(double value) : value(value) {}
- ConstantType getType() override
- {
- return ConstantType::ConstantNumber;
- }
- void writeData(ByteStream& buffer) override
- {
- buffer << value;
- }
- bool equals(Constant* rhs) override
- {
- if (auto other = dynamic_cast<ConstantNumber*>(rhs))
- {
- return value == other->value;
- }
- return false;
- }
- };
- struct ConstantString : Constant
- {
- std::string value;
- Compiler& c;
- ConstantString(const std::string& value, Compiler& c) : value(value), c(c)
- {
- auto& strings = c.strings;
- auto it = std::find(strings.begin(), strings.end(), value);
- if (it == strings.end())
- strings.push_back(value);
- }
- ConstantType getType() override
- {
- return ConstantType::ConstantString;
- }
- void writeData(ByteStream& buffer) override
- {
- auto& strings = c.strings;
- auto it = std::find_if(strings.begin(), strings.end(),
- [&](const std::string& str)
- {
- return str == value;
- });
- if (it == strings.end())
- throw runtime_error("could not find string in strings table.");
- buffer << int(std::distance(strings.begin(), it) + 1);
- }
- bool equals(Constant* rhs) override
- {
- if (auto other = dynamic_cast<ConstantString*>(rhs))
- {
- return value == other->value;
- }
- return false;
- }
- };
- struct ConstantGlobal : Constant
- {
- uint32_t idx;
- ConstantGlobal(uint32_t idx) : idx(idx) {}
- ConstantType getType() override
- {
- return ConstantType::ConstantGlobal;
- }
- void writeData(ByteStream& buffer) override
- {
- buffer << idx;
- }
- bool equals(Constant* rhs) override
- {
- if (auto other = dynamic_cast<ConstantGlobal*>(rhs))
- {
- return idx == other->idx;
- }
- return false;
- }
- };
- struct ConstantHashTable : Constant
- {
- std::vector<uint32_t> stringIndices;
- ConstantHashTable() = default;
- ConstantType getType() override
- {
- return ConstantType::ConstantHashTable;
- }
- void writeData(ByteStream& buffer) override
- {
- buffer << int(stringIndices.size());
- for (auto i : stringIndices)
- {
- // pretty sure this is an int (based on minimal reversing)
- buffer << int(i);
- }
- }
- bool equals(Constant* rhs) override
- {
- if (auto other = dynamic_cast<ConstantHashTable*>(rhs))
- {
- // checking size equality first is probably more efficient? idk
- return stringIndices.size() == other->stringIndices.size()
- && stringIndices == other->stringIndices;
- }
- return false;
- }
- };
- struct FunctionPrototype
- {
- byte regCount = 0;
- byte maxRegCount = 0;
- byte argCount = 0;
- byte isVarArg = 0;
- std::optional<std::string> name;
- std::map<std::string, byte> passedUpvals;
- std::vector<std::string> upvals;
- std::vector<uint32_t> code;
- phmap::flat_hash_map<std::string, byte> locals;
- std::vector<Constant*> constants;
- unsigned int lastLine = 0;
- std::vector<size_t> lineInfo;
- std::vector<std::string> modifiedGlobals;
- std::vector<ScriptParser::AstExprFunction*> closures;
- std::vector<size_t> jumpOutQueue;
- byte loopDepth = 0;
- ScriptParser::Allocator& a;
- Compiler& compiler;
- FunctionPrototype(ScriptParser::Allocator& a, Compiler& compiler) : a(a), compiler(compiler) {}
- byte allocateRegisters(byte c)
- {
- auto ret = regCount;
- if (regCount + c > MAX_REG_COUNT)
- {
- throw runtime_error(
- "Out of registers when trying to allocate %d registers: exceeded limit %d",
- c, MAX_REG_COUNT);
- }
- regCount += c;
- if (regCount > maxRegCount)
- maxRegCount = regCount;
- return ret;
- }
- byte freeRegisters(byte c)
- {
- if (regCount - c < 0)
- {
- throw runtime_error(
- "Attempt to deallocate %d registers: action puts registers below minimum 0",
- c);
- }
- regCount -= c;
- return regCount;
- }
- void assignLocal(int reg_idx, const std::string& name)
- {
- if (locals.size() > MAX_LOCAL_COUNT)
- throw runtime_error("Out of local variables: exceeded limit %d",
- MAX_LOCAL_COUNT);
- locals[name] = reg_idx;
- }
- int getOrCreateClosure(ScriptParser::AstExprFunction* funcExpr)
- {
- auto it = std::find(closures.begin(), closures.end(), funcExpr);
- if (it == closures.end())
- {
- closures.push_back(funcExpr);
- return closures.size() - 1;
- }
- return std::distance(closures.begin(), it);
- }
- static uint32_t encodeGlobalConstantIndex(uint32_t idx)
- {
- return (idx | 0x400) << 20;
- }
- static uint32_t encodeGlobalConstantIndex(uint32_t idx, uint32_t idx1)
- {
- return (idx | 0x800) << 20 | idx1 << 10;
- }
- static uint32_t encodeGlobalConstantIndex(uint32_t idx, uint32_t idx1, uint32_t idx2)
- {
- return (idx | 0xC00) << 20 | idx1 << 10 | idx2;
- }
- uint32_t hashString(const char* str)
- {
- uint32_t result = strlen(str);
- uint32_t v3 = (result >> 5) + 1;
- for (auto i = strlen(str); i >= v3; i -= v3)
- {
- auto v4 = (unsigned __int8)str[i - 1];
- result ^= (result >> 2) + 32 * result + v4;
- }
- return result;
- }
- uint32_t getOrCreateConstant(ScriptParser::AstExpr* constExpr)
- {
- if (constExpr->is<ScriptParser::AstExprConstantNil>())
- {
- return getOrCreateConstant(new (a) ConstantNil{});
- }
- if (auto boolConstExpr = constExpr->as<ScriptParser::AstExprConstantBool>())
- {
- return getOrCreateConstant(new (a) ConstantBoolean{ boolConstExpr->value });
- }
- if (auto numConstExpr = constExpr->as<ScriptParser::AstExprConstantNumber>())
- {
- return getOrCreateConstant(new (a) ConstantNumber{ numConstExpr->value });
- }
- if (auto stringConstExpr = constExpr->as<ScriptParser::AstExprConstantString>())
- {
- return getOrCreateConstant(new (a) ConstantString{
- {stringConstExpr->value.data, stringConstExpr->value.size}, compiler
- });
- }
- if (auto globalConstExpr = constExpr->as<ScriptParser::AstExprGlobal>())
- {
- auto constantIdx = getOrCreateConstant(new (a)
- ConstantString{ globalConstExpr->name.value, compiler });
- auto encodedConstantIdx = encodeGlobalConstantIndex(constantIdx);
- return getOrCreateConstant(new (a) ConstantGlobal{ encodedConstantIdx });
- }
- return -1;
- }
- uint32_t getOrCreateConstant(Constant* constant)
- {
- auto it = std::find_if(constants.begin(), constants.end(),
- [&](Constant* c)
- {
- return constant->equals(c);
- });
- if (it != constants.end())
- return std::distance(constants.begin(), it);
- constants.push_back(constant);
- return constants.size() - 1;
- }
- uint32_t getOrCreateConstant(double val)
- {
- auto constant = new (a) ConstantNumber{ val };
- auto it = std::find_if(constants.begin(), constants.end(),
- [&](Constant* c)
- {
- return constant->equals(c);
- });
- if (it != constants.end())
- {
- return std::distance(constants.begin(), it);
- }
- constants.push_back(constant);
- return constants.size() - 1;
- }
- uint32_t getOrCreateConstant(ScriptParser::AstArray<char> val)
- {
- auto constant = new (a) ConstantString{ std::string{ val.data, val.size }, compiler };
- auto it = std::find_if(constants.begin(), constants.end(),
- [&](Constant* c)
- {
- return constant->equals(c);
- });
- if (it != constants.end())
- {
- return std::distance(constants.begin(), it);
- }
- constants.push_back(constant);
- return constants.size() - 1;
- }
- void writeInstruction32(const Instruction32& instr,
- const ScriptParser::Position& pos)
- {
- lineInfo.push_back(pos.line - lastLine);
- lastLine = pos.line;
- #ifndef _DEBUG
- if (!instr.extraData)
- {
- Instruction32 instrCopy = instr;
- instrCopy.op = OpCode(getClientOp(instr.op));
- code.push_back(instrCopy.encoded);
- }
- else
- #endif
- {
- code.push_back(instr.encoded);
- }
- }
- void writeInstruction32(const Instruction32& instr)
- {
- lineInfo.push_back(0);
- #ifndef _DEBUG
- if (!instr.extraData)
- {
- Instruction32 instrCopy = instr;
- instrCopy.op = OpCode(getClientOp(instr.op));
- code.push_back(instrCopy.encoded);
- }
- else
- #endif
- {
- code.push_back(instr.encoded);
- }
- }
- void compileIfStat(ScriptParser::AstStatIf* ifStat)
- {
- auto conditionExpr = ifStat->condition;
- while (auto groupExpr = conditionExpr->as<ScriptParser::AstExprGroup>())
- {
- conditionExpr = groupExpr->expr;
- }
- // NOTE: now handled in parser
- /*
- auto boolExpr = conditionExpr->as<ScriptParser::AstExprConstantBool>();
- if (conditionExpr->is<ScriptParser::AstExprConstantNumber>()
- || (boolExpr
- && boolExpr->value))
- {
- compileStatement(ifStat->thenbody);
- locals = localsBackup;
- return;
- }
- */
- if (!ifStat->elsebody && ParserUtils::statBreaks(ifStat->thenbody)
- && loopDepth != 0)
- {
- compileConditionExpr(conditionExpr, nullptr, jumpOutQueue, true);
- return;
- }
- std::vector<size_t> jumpIndicies;
- compileConditionExpr(conditionExpr, nullptr, jumpIndicies, false);
- compileStatement(ifStat->thenbody);
- if (ifStat->elsebody)
- {
- if (ParserUtils::statReturns(ifStat->thenbody))
- {
- calculateJumps(jumpIndicies, code.size());
- compileStatement(ifStat->elsebody);
- }
- else
- {
- auto thenJumpIndex = code.size();
- writeInstruction32({ OpCode::Jump, 0, int16_t(0) },
- ifStat->thenbody->location.end);
- calculateJumps(jumpIndicies, code.size());
- compileStatement(ifStat->elsebody);
- calculateJump(thenJumpIndex, code.size());
- }
- }
- else
- {
- calculateJumps(jumpIndicies, code.size());
- }
- }
- void compileWhileStat(ScriptParser::AstStatWhile* whileStat)
- {
- auto conditionExpr = whileStat->condition;
- while (auto groupExpr = conditionExpr->as<ScriptParser::AstExprGroup>())
- {
- conditionExpr = groupExpr->expr;
- }
- auto jumpOutQueueStart = jumpOutQueue.size();
- size_t loopEndIndex;
- auto constEvalRes = conditionExpr->constEval();
- if (constEvalRes == ScriptParser::ConstEvalResult::True)
- {
- auto loopStartIndex = code.size();
- auto localsBackup = locals;
- loopDepth++;
- compileStatement(whileStat->body);
- loopDepth--;
- locals = localsBackup;
- auto loopJumpBackIndex = code.size();
- writeInstruction32({ OpCode::LoopJump, 0, int16_t(0) },
- whileStat->location.end);
- loopEndIndex = code.size();
- calculateJump(loopJumpBackIndex, loopStartIndex);
- }
- else if (constEvalRes == ScriptParser::ConstEvalResult::Invalid)
- {
- auto loopStartIndex = code.size();
- std::vector<size_t> jumpIndicies;
- compileConditionExpr(conditionExpr, nullptr, jumpIndicies, false);
- auto localsBackup = locals;
- loopDepth++;
- compileStatement(whileStat->body);
- loopDepth--;
- locals = localsBackup;
- auto loopJumpBackIndex = code.size();
- writeInstruction32({ OpCode::LoopJump, 0, int16_t(0) },
- whileStat->location.end);
- calculateJump(loopJumpBackIndex, loopStartIndex);
- loopEndIndex = code.size();
- calculateJumps(jumpIndicies, loopEndIndex);
- }
- else
- {
- return;
- }
- for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
- {
- calculateJump(jumpOutQueue.at(i), loopEndIndex);
- }
- jumpOutQueue.resize(jumpOutQueueStart);
- }
- void compileConditionExpr(ScriptParser::AstExpr* conditionExpr, byte* reg,
- std::vector<size_t>& relocations, bool invert)
- {
- if (auto binaryExpr = conditionExpr->as<ScriptParser::AstExprBinary>())
- {
- switch (binaryExpr->op)
- {
- case ScriptParser::AstExprBinary::Add: break;
- case ScriptParser::AstExprBinary::Sub: break;
- case ScriptParser::AstExprBinary::Mul: break;
- case ScriptParser::AstExprBinary::Div: break;
- case ScriptParser::AstExprBinary::Mod: break;
- case ScriptParser::AstExprBinary::Pow: break;
- case ScriptParser::AstExprBinary::Concat: break;
- case ScriptParser::AstExprBinary::CompareNe:
- case ScriptParser::AstExprBinary::CompareEq:
- case ScriptParser::AstExprBinary::CompareLt:
- case ScriptParser::AstExprBinary::CompareLe:
- case ScriptParser::AstExprBinary::CompareGt:
- case ScriptParser::AstExprBinary::CompareGe:
- relocations.push_back(compileCompareExpr(binaryExpr, !invert));
- return;
- case ScriptParser::AstExprBinary::And:
- case ScriptParser::AstExprBinary::Or:
- {
- if (invert == (binaryExpr->op == ScriptParser::AstExprBinary::And))
- {
- std::vector<size_t> tempRelocations;
- compileConditionExpr(binaryExpr->left, nullptr, tempRelocations,
- !invert);
- compileConditionExpr(binaryExpr->right, reg, relocations, invert);
- calculateJumps(tempRelocations, code.size());
- }
- else
- {
- compileConditionExpr(binaryExpr->left, reg, relocations, invert);
- compileConditionExpr(binaryExpr->right, reg, relocations, invert);
- }
- return;
- }
- default:;
- }
- }
- if (auto unaryExpr = conditionExpr->as<ScriptParser::AstExprUnary>())
- {
- if (unaryExpr->op == ScriptParser::AstExprUnary::Not)
- {
- return compileConditionExpr(unaryExpr->expr, reg, relocations, !invert);
- }
- }
- auto regBackup = regCount;
- byte conditionReg;
- if (reg)
- {
- conditionReg = *reg;
- compileExpression(conditionExpr, conditionReg);
- }
- else
- {
- conditionReg =
- getLocalOrCompileExpression(conditionExpr, allocateRegisters(1));
- }
- relocations.push_back(code.size());
- writeInstruction32({ invert ? OpCode::Test : OpCode::NotTest, conditionReg,
- int16_t(0) }, conditionExpr->location.end);
- regCount = regBackup;
- return;
- }
- // returns true if compiled as a tail expression
- bool compileTailExpression(ScriptParser::AstExpr* expr, byte reg)
- {
- if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
- {
- compileCallExpr(callExpr, reg, 0, false, true);
- return true;
- }
- if (expr->is<ScriptParser::AstExprVarargs>())
- {
- writeInstruction32({ OpCode::LoadVarargs, reg, 0, 0 }, expr->location.begin);
- return true;
- }
- compileExpression(expr, reg);
- return false;
- }
- void compileExpression(ScriptParser::AstExpr* expr, byte reg)
- {
- if (auto groupExpr = expr->as<ScriptParser::AstExprGroup>())
- {
- compileExpression(groupExpr->expr, reg);
- }
- else if (expr->is<ScriptParser::AstExprConstantNil>())
- {
- writeInstruction32({ OpCode::LoadNil, reg, 0, 0 }, expr->location.begin);
- }
- else if (auto boolExpr = expr->as<ScriptParser::AstExprConstantBool>())
- {
- writeInstruction32({ OpCode::LoadBool, reg, boolExpr->value, 0 },
- expr->location.begin);
- }
- else if (auto numExpr = expr->as<ScriptParser::AstExprConstantNumber>())
- {
- if (numExpr->value <= SHRT_MAX
- && numExpr->value >= SHRT_MIN
- && floor(numExpr->value) == numExpr->value) // is whole
- {
- writeInstruction32({ OpCode::LoadShort, reg, int16_t(numExpr->value) },
- expr->location.begin);
- return;
- }
- auto constIndex = getOrCreateConstant(numExpr->value);
- if (constIndex <= UINT16_MAX)
- {
- writeInstruction32({ OpCode::LoadConst, reg,
- uint16_t(constIndex) }, expr->location.begin);
- }
- else
- {
- writeInstruction32({ OpCode::LoadConstLarge, reg, uint16_t(0) },
- expr->location.begin);
- writeInstruction32(constIndex);
- }
- }
- else if (auto strExpr = expr->as<ScriptParser::AstExprConstantString>())
- {
- auto constIndex = getOrCreateConstant(strExpr->value);
- if (constIndex <= UINT16_MAX)
- {
- writeInstruction32({ OpCode::LoadConst, reg,
- uint16_t(constIndex) }, expr->location.begin);
- }
- else
- {
- writeInstruction32({ OpCode::LoadConstLarge, reg, uint16_t(0) },
- expr->location.begin);
- writeInstruction32(constIndex);
- }
- }
- else if (auto localExpr = expr->as<ScriptParser::AstExprLocal>())
- {
- compileLocalExpr(localExpr, reg);
- }
- else if (auto globalExpr = expr->as<ScriptParser::AstExprGlobal>())
- {
- compileGlobalExpr(globalExpr, reg);
- }
- else if (auto varargsExpr = expr->as<ScriptParser::AstExprVarargs>())
- {
- writeInstruction32({ OpCode::LoadVarargs, reg, 2, 0 },
- varargsExpr->location.begin);
- }
- else if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
- {
- compileCallExpr(callExpr, reg, 1, false, false);
- }
- else if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
- {
- compileIndexNameExpr(indexNameExpr, reg);
- }
- else if (auto indexExpr = expr->as<ScriptParser::AstExprIndexExpr>())
- {
- compileIndexExpr(indexExpr, reg);
- }
- else if (auto funcExpr = expr->as<ScriptParser::AstExprFunction>())
- {
- compileFunctionExprRef(funcExpr, reg);
- }
- else if (auto tableExpr = expr->as<ScriptParser::AstExprTable>())
- {
- compileTableExpr(tableExpr, reg);
- }
- else if (auto unaryExpr = expr->as<ScriptParser::AstExprUnary>())
- {
- compileUnaryExpr(unaryExpr, reg);
- }
- else if (auto binaryExpr = expr->as<ScriptParser::AstExprBinary>())
- {
- compileBinaryExpr(binaryExpr, reg);
- }
- }
- byte getLocalOrCompileExpression(ScriptParser::AstExpr* expr, byte reg)
- {
- if (auto localExpr = expr->as<ScriptParser::AstExprLocal>())
- {
- auto it = locals.find(localExpr->local->name.value);
- if (it != locals.end())
- {
- return it->second;
- }
- }
- compileExpression(expr, reg);
- return reg;
- }
- void compileBinaryExpr(ScriptParser::AstExprBinary* binaryExpr, byte reg)
- {
- switch (binaryExpr->op)
- {
- case ScriptParser::AstExprBinary::Add:
- case ScriptParser::AstExprBinary::Sub:
- case ScriptParser::AstExprBinary::Mul:
- case ScriptParser::AstExprBinary::Div:
- case ScriptParser::AstExprBinary::Mod:
- case ScriptParser::AstExprBinary::Pow:
- {
- auto lhs = getLocalOrCompileExpression(binaryExpr->left, reg);
- auto rhs = binaryExpr->right;
- // yay optimization
- /*if (auto numExpr = rhs->as<ScriptParser::AstExprConstantNumber>();
- numExpr
- && floor(numExpr->value) == numExpr->value
- && numExpr->value < UCHAR_MAX)
- {
- static OpCode binaryOpByteMap[6] {
- OpCode::AddByte, OpCode::SubByte, OpCode::MulByte,
- OpCode::DivByte, OpCode::ModByte, OpCode::PowByte
- };
- writeInstruction32({ binaryOpByteMap[binaryExpr->op], reg, lhs,
- byte(numExpr->value) }, binaryExpr->location.begin);
- }
- else*/
- {
- static OpCode binaryOpMap[6]{
- OpCode::Add, OpCode::Sub, OpCode::Mul,
- OpCode::Div, OpCode::Mod, OpCode::Pow
- };
- auto rhsReg = getLocalOrCompileExpression(rhs, allocateRegisters(1));
- writeInstruction32({ binaryOpMap[binaryExpr->op], reg, lhs,
- rhsReg }, binaryExpr->location.begin);
- freeRegisters(1);
- }
- break;
- }
- case ScriptParser::AstExprBinary::Concat:
- {
- auto concatDepth = ParserUtils::getBinaryExprDepth(binaryExpr,
- ScriptParser::AstExprBinary::Concat, ParserUtils::RecurseDirection::Right);
- if (concatDepth > 2)
- {
- auto regBackup = regCount;
- auto c = binaryExpr;
- while (c != nullptr
- && c->op == ScriptParser::AstExprBinary::Concat)
- {
- compileExpression(c->left, allocateRegisters(1));
- if (auto next = c->right->as<ScriptParser::AstExprBinary>())
- {
- c = next;
- }
- else
- {
- break;
- }
- }
- compileExpression(c->right, allocateRegisters(1));
- writeInstruction32({ OpCode::Concat, reg,
- byte(reg + 1), byte(reg + concatDepth) });
- regCount = regBackup;
- break;
- }
- compileExpression(binaryExpr->left, allocateRegisters(1));
- compileExpression(binaryExpr->right, allocateRegisters(1));
- writeInstruction32({ OpCode::Concat, reg,
- byte(reg + 1), byte(reg + 2) });
- freeRegisters(2);
- break;
- }
- case ScriptParser::AstExprBinary::CompareNe:
- case ScriptParser::AstExprBinary::CompareEq:
- case ScriptParser::AstExprBinary::CompareLt:
- case ScriptParser::AstExprBinary::CompareLe:
- case ScriptParser::AstExprBinary::CompareGt:
- case ScriptParser::AstExprBinary::CompareGe:
- {
- auto compIndex = compileCompareExpr(binaryExpr, false);
- writeInstruction32({ OpCode::LoadBool, reg, 0, 1 },
- binaryExpr->location.begin);
- auto trueCaseIndex = code.size();
- writeInstruction32({ OpCode::LoadBool, reg, 1, 0 },
- binaryExpr->location.begin);
- calculateJump(compIndex, trueCaseIndex);
- break;
- }
- case ScriptParser::AstExprBinary::And:
- case ScriptParser::AstExprBinary::Or:
- {
- compileLogicalBinaryExpr(binaryExpr, reg);
- break;
- }
- }
- }
- void compileLogicalBinaryExpr(ScriptParser::AstExprBinary* binaryExpr, byte reg)
- {
- /*auto depth = ParserUtils::getBinaryExprDepth(binaryExpr,
- binaryExpr->op, ParserUtils::RecurseDirection::Left);
- if (depth > 2)
- {
- std::vector<ScriptParser::AstExpr*> exprList;
- auto c = binaryExpr;
- while (true)
- {
- exprList.push_back(c->right);
- if (auto next = c->left->as<ScriptParser::AstExprBinary>())
- {
- c = next;
- }
- else
- {
- break;
- }
- }
- exprList.push_back(c->left);
- std::reverse(exprList.begin(), exprList.end());
- std::vector<size_t> testIndexList;
- testIndexList.reserve(exprList.size() - 1);
- for (auto it = exprList.cbegin();; ++it)
- {
- compileExpression(*it, reg);
- if (it + 1 == exprList.cend())
- {
- break;
- }
- testIndexList.push_back(code.size());
- writeInstruction32({
- binaryExpr->op == ScriptParser::AstExprBinary::And
- ? OpCode::NotTest : OpCode::Test, reg, int16_t(0) },
- binaryExpr->left->location.end);
- }
- auto endIndex = code.size();
- for (auto testIndex : testIndexList)
- {
- calculateJump(testIndex, endIndex);
- }
- return;
- }*/
- compileExpression(binaryExpr->left, reg);
- auto testIndex = code.size();
- writeInstruction32({
- binaryExpr->op == ScriptParser::AstExprBinary::And
- ? OpCode::NotTest : OpCode::Test, reg, int16_t(0) },
- binaryExpr->left->location.end);
- compileExpression(binaryExpr->right, reg);
- auto endIndex = code.size();
- calculateJump(testIndex, endIndex);
- }
- void calculateJump(size_t fromIndex, size_t toIndex)
- {
- int jumpDistance = toIndex - fromIndex - 1;
- if ((int16_t)jumpDistance == jumpDistance)
- {
- Instruction32 instr = code[fromIndex];
- instr.b_x = (uint16_t)jumpDistance;
- code[fromIndex] = instr.encoded;
- }
- else
- {
- // if (abs(jumpDistance) >= 0x800000)
- {
- throw RBX::runtime_error("Jump distance is too large: %d",
- jumpDistance);
- }
- // TODO: big jumps
- }
- }
- void calculateJumps(const std::vector<size_t> fromIndicies, size_t toIndex)
- {
- for (auto fromIndex : fromIndicies)
- {
- calculateJump(fromIndex, toIndex);
- }
- }
- size_t compileCompareExpr(ScriptParser::AstExprBinary* binaryExpr, bool invert)
- {
- auto regBackup = regCount;
- auto leftReg = getLocalOrCompileExpression(binaryExpr->left, allocateRegisters(1));
- auto rightReg = getLocalOrCompileExpression(binaryExpr->right, allocateRegisters(1));
- OpCode op = OpCode::Nop;
- switch (binaryExpr->op)
- {
- case ScriptParser::AstExprBinary::CompareNe:
- op = OpCode::NotEqual;
- if (invert)
- op = OpCode::Equal;
- break;
- case ScriptParser::AstExprBinary::CompareEq:
- op = OpCode::Equal;
- if (invert)
- op = OpCode::NotEqual;
- break;
- case ScriptParser::AstExprBinary::CompareLt:
- case ScriptParser::AstExprBinary::CompareGt:
- op = OpCode::LesserThan;
- if (invert)
- op = OpCode::GreaterOrEqual;
- break;
- case ScriptParser::AstExprBinary::CompareLe:
- case ScriptParser::AstExprBinary::CompareGe:
- op = OpCode::LesserOrEqual;
- if (invert)
- op = OpCode::GreaterThan;
- break;
- default:;
- }
- auto compIndex = code.size();
- if (binaryExpr->op == ScriptParser::AstExprBinary::CompareGt
- || binaryExpr->op == ScriptParser::AstExprBinary::CompareGe)
- {
- writeInstruction32({ op, rightReg, int16_t(0) },
- binaryExpr->location.begin);
- writeInstruction32(leftReg,
- binaryExpr->location.begin);
- }
- else
- {
- writeInstruction32({ op, leftReg, int16_t(0) },
- binaryExpr->location.begin);
- writeInstruction32(rightReg,
- binaryExpr->location.begin);
- }
- regCount = regBackup;
- return compIndex;
- }
- void compileUnaryExpr(ScriptParser::AstExprUnary* unaryExpr, byte reg)
- {
- static OpCode unaryOpMap[3]{
- OpCode::Not, OpCode::UnaryMinus, OpCode::Len
- };
- if (unaryExpr->op == ScriptParser::AstExprUnary::Not)
- {
- if (unaryExpr->expr->is<ScriptParser::AstExprConstantNumber>())
- {
- writeInstruction32({ OpCode::LoadBool, reg, 0, 0 },
- unaryExpr->expr->location.begin);
- return;
- }
- if (unaryExpr->expr->is<ScriptParser::AstExprConstantNil>())
- {
- writeInstruction32({ OpCode::LoadBool, reg, 1, 0 },
- unaryExpr->expr->location.begin);
- return;
- }
- if (auto boolExpr = unaryExpr->expr->as<ScriptParser::AstExprConstantBool>())
- {
- writeInstruction32({ OpCode::LoadBool, reg, !boolExpr->value, 0 },
- unaryExpr->expr->location.begin);
- return;
- }
- }
- auto regBackup = regCount;
- writeInstruction32({ unaryOpMap[unaryExpr->op], reg,
- getLocalOrCompileExpression(unaryExpr->expr, allocateRegisters(1)), 0 },
- unaryExpr->location.begin);
- regCount = regBackup;
- }
- void compileTableExpr(ScriptParser::AstExprTable* tableExpr, byte reg)
- {
- if (tableExpr->pairs.size == 0)
- {
- writeInstruction32({ OpCode::NewTable, reg, 0, 0 },
- tableExpr->location.begin);
- writeInstruction32(0);
- return;
- }
- bool useHashOptimization = true;
- byte hashSize = 0;
- size_t arraySize = 0;
- for (size_t i = 0; i < tableExpr->pairs.size; i += 2)
- {
- if (auto key = tableExpr->pairs.data[i])
- {
- if (!key->is<ScriptParser::AstExprConstantString>())
- {
- useHashOptimization = false;
- }
- hashSize++;
- }
- else
- {
- useHashOptimization = false;
- arraySize++;
- }
- }
- if (useHashOptimization)
- {
- auto hashTable = new (a) ConstantHashTable{};
- for (auto i = 0; i < hashSize * 2; i += 2)
- {
- auto stringConst =
- tableExpr->pairs.data[i]->as<ScriptParser::AstExprConstantString>();
- auto stringConstValue =
- std::string{ stringConst->value.data, stringConst->value.size };
- auto stringConstIdx = getOrCreateConstant(new (a)
- ConstantString{ stringConstValue, compiler });
- hashTable->stringIndices.push_back(stringConstIdx);
- }
- auto constIndex = getOrCreateConstant(hashTable);
- if (constIndex > UINT16_MAX)
- {
- goto normal;
- }
- writeInstruction32({ OpCode::NewTableConst, reg,
- uint16_t(constIndex) }, tableExpr->location.begin);
- for (auto i = 0; i < hashSize * 2; i += 2)
- {
- auto stringConst =
- tableExpr->pairs.data[i]->as<ScriptParser::AstExprConstantString>();
- auto stringConstValue =
- std::string{ stringConst->value.data, stringConst->value.size };
- auto stringConstIdx = getOrCreateConstant(new (a)
- ConstantString{ stringConstValue, compiler });
- auto valReg = allocateRegisters(1);
- compileExpression(tableExpr->pairs.data[i + 1], valReg);
- writeInstruction32({ OpCode::SetTableIndexConstant, valReg,
- reg, byte(hashString(stringConstValue.c_str())) });
- writeInstruction32({ stringConstIdx });
- freeRegisters(1);
- }
- return;
- }
- normal:
- byte encodedHashSize = 0;
- do
- {
- encodedHashSize++;
- } while (1 << encodedHashSize < hashSize);
- writeInstruction32({ OpCode::NewTable, reg,
- byte(hashSize ? encodedHashSize + 1 : 0), 0 },
- tableExpr->location.begin);
- writeInstruction32(arraySize);
- auto regBackup = regCount;
- size_t arrayIndex = 0;
- size_t flushIndex = 0;
- size_t insertLocation = 1;
- byte arrayInc = std::min<byte>(16, MAX_REG_COUNT - regCount);
- bool wasTail = false;
- for (size_t i = 0; i < tableExpr->pairs.size; i += 2)
- {
- auto key = tableExpr->pairs.data[i];
- auto val = tableExpr->pairs.data[i + 1];
- if (key)
- {
- auto keyReg = allocateRegisters(1);
- auto valReg = allocateRegisters(1);
- compileExpression(key, keyReg);
- compileExpression(val, valReg);
- writeInstruction32({ OpCode::SetTableIndex, valReg, reg, keyReg },
- key->location.begin);
- freeRegisters(2);
- }
- else if (i + 2 == tableExpr->pairs.size)
- {
- wasTail = compileTailExpression(val, allocateRegisters(1));
- flushIndex++;
- arrayIndex++;
- }
- else
- {
- compileExpression(val, allocateRegisters(1));
- flushIndex++;
- arrayIndex++;
- if (arrayIndex % arrayInc == 0)
- {
- auto count = byte(arrayInc);
- if (!wasTail)
- {
- count++;
- }
- writeInstruction32({ OpCode::SetList, reg, byte(reg + 1),
- byte(wasTail ? 0 : count) });
- writeInstruction32(insertLocation);
- insertLocation = arrayIndex + 1;
- flushIndex = 0;
- regCount = regBackup;
- }
- }
- }
- if (flushIndex)
- {
- auto count = byte(flushIndex + 1);
- writeInstruction32({ OpCode::SetList, reg, byte(reg + 1),
- byte(wasTail ? 0 : count) });
- writeInstruction32(insertLocation);
- }
- regCount = regBackup;
- }
- void compileLocalExpr(ScriptParser::AstExprLocal* localExpr, byte reg, bool set = false)
- {
- if (localExpr->upvalue) // is possibly an upvalue
- {
- // locals are prioritized over upvalues
- auto it = locals.find(localExpr->local->name.value);
- if (it != locals.end())
- {
- if (set)
- {
- writeInstruction32({ OpCode::Move, it->second,
- reg, 0 },
- localExpr->location.begin);
- }
- else
- {
- writeInstruction32({ OpCode::Move, reg,
- it->second, 0 },
- localExpr->location.begin);
- }
- }
- else
- {
- auto upvalIt =
- std::find(upvals.begin(), upvals.end(),
- localExpr->local->name.value);
- if (upvalIt == upvals.end())
- {
- upvals.push_back(localExpr->local->name.value);
- upvalIt = upvals.end() - 1;
- }
- if (set)
- {
- writeInstruction32({ OpCode::SetUpvalue, reg,
- byte(std::distance(upvals.begin(), upvalIt)), 0 },
- localExpr->location.begin);
- }
- else
- {
- writeInstruction32({ OpCode::GetUpvalue, reg,
- byte(std::distance(upvals.begin(), upvalIt)), 0 },
- localExpr->location.begin);
- }
- }
- }
- else
- {
- if (!set)
- {
- writeInstruction32({ OpCode::Move, reg,
- locals.at(localExpr->local->name.value), 0 },
- localExpr->location.begin);
- }
- else
- {
- writeInstruction32({ OpCode::Move, locals.at(localExpr->local->name.value),
- reg, 0 },
- localExpr->location.begin);
- }
- }
- }
- void compileFunctionExprRef(ScriptParser::AstExprFunction* funcExpr, byte reg)
- {
- auto closureIdx = getOrCreateClosure(funcExpr);
- writeInstruction32({ OpCode::Closure, reg, uint16_t(closureIdx) },
- funcExpr->location.begin);
- auto proto = compiler.protos.at(funcExpr);
- for (auto upval : proto.upvals)
- {
- auto it = locals.find(upval);
- if (it != locals.end())
- {
- writeInstruction32({ OpCode::Move, 0,
- it->second, 0 },
- funcExpr->location.begin);
- passedUpvals[upval] = it->second;
- }
- else
- {
- auto upvalIt =
- std::find(upvals.begin(), upvals.end(),
- upval);
- if (upvalIt == upvals.end())
- {
- upvals.push_back(upval);
- upvalIt = upvals.end() - 1;
- }
- writeInstruction32({ OpCode::GetUpvalue, 0,
- byte(std::distance(upvals.begin(), upvalIt)), 0 },
- funcExpr->location.begin);
- }
- }
- }
- void compileExpressionIndexOptimized(ScriptParser::AstExpr* expr, byte reg)
- {
- // faster to have common expressions at the top
- if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
- {
- compileIndexNameExpr(indexNameExpr, reg);
- }
- else if (auto indexExpr = expr->as<ScriptParser::AstExprIndexExpr>())
- {
- compileIndexExpr(indexExpr, reg);
- }
- else if (auto globalExpr = expr->as<ScriptParser::AstExprGlobal>())
- {
- compileGlobalExpr(globalExpr, reg);
- }
- else if (auto localExpr = expr->as<ScriptParser::AstExprLocal>())
- {
- compileLocalExpr(localExpr, reg);
- }
- else
- {
- compileExpression(expr, reg);
- }
- }
- void compileIndexExpr(ScriptParser::AstExprIndexExpr* indexExpr, byte reg)
- {
- auto regBackup = regCount;
- auto indexReg = getLocalOrCompileExpression(indexExpr->expr, allocateRegisters(1));
- auto numConstExpr = indexExpr->index->as<ScriptParser::AstExprConstantNumber>();
- if (numConstExpr
- && floor(numConstExpr->value) == numConstExpr->value
- && numConstExpr->value >= 1
- && numConstExpr->value <= UCHAR_MAX + 1)
- {
- writeInstruction32({ OpCode::GetTableIndexByte, reg, indexReg,
- byte(numConstExpr->value - 1) }, numConstExpr->location.begin);
- }
- else
- {
- auto indexValue = getLocalOrCompileExpression(indexExpr->index, allocateRegisters(1));
- writeInstruction32({ OpCode::GetTableIndex, reg, indexReg, indexValue },
- indexExpr->index->location.begin);
- }
- regCount = regBackup;
- }
- void compileIndexNameExpr(ScriptParser::AstExprIndexName* indexNameExpr, byte reg)
- {
- if (ParserUtils::getIndexNameExprDepth(indexNameExpr) <= 3
- && ParserUtils::exprContainsGlobal(indexNameExpr))
- {
- std::vector<std::string> indexStrings{};
- ScriptParser::AstExprIndexName* c = indexNameExpr;
- while (c != nullptr)
- {
- indexStrings.push_back(c->index.value);
- if (auto globalExpr = c->expr->as<ScriptParser::AstExprGlobal>())
- {
- indexStrings.push_back(globalExpr->name.value);
- break;
- }
- c = c->expr->as<ScriptParser::AstExprIndexName>();
- }
- // check if builtin
- static auto LuauBuiltinGlobals = std::array<std::string, 8>
- {
- "Game", "Workspace", "_G",
- "game", "plugin", "script",
- "shared", "workspace"
- };
- auto bgIt = std::find(LuauBuiltinGlobals.cbegin(),
- LuauBuiltinGlobals.cend(), indexStrings.back());
- if (bgIt == LuauBuiltinGlobals.cend())
- {
- std::reverse(indexStrings.begin(), indexStrings.end());
- auto constantIdx1 = getOrCreateConstant(new (a)
- ConstantString{ indexStrings[0], compiler });
- auto constantIdx2 = getOrCreateConstant(new (a)
- ConstantString{ indexStrings[1], compiler });
- uint32_t encodedConstIdx;
- if (indexStrings.size() == 2)
- {
- encodedConstIdx = encodeGlobalConstantIndex(constantIdx1,
- constantIdx2);
- }
- else // 3
- {
- auto constantIdx3 = getOrCreateConstant(new (a)
- ConstantString{ indexStrings[2], compiler });
- encodedConstIdx = encodeGlobalConstantIndex(constantIdx1,
- constantIdx2, constantIdx3);
- }
- auto globalConstIdx =
- getOrCreateConstant(new (a) ConstantGlobal{ encodedConstIdx });
- if (globalConstIdx < 0x8000)
- {
- writeInstruction32({ OpCode::GetGlobalConst, reg,
- uint16_t(globalConstIdx) }, indexNameExpr->location.begin);
- writeInstruction32(encodedConstIdx,
- indexNameExpr->location.begin);
- return;
- }
- }
- }
- auto regBackup = regCount;
- allocateRegisters(1);
- auto idxReg = getLocalOrCompileExpression(indexNameExpr->expr, byte(reg + 1));
- auto constantIdx = getOrCreateConstant(new (a)
- ConstantString{ indexNameExpr->index.value, compiler });
- writeInstruction32({ OpCode::GetTableIndexConstant, reg, idxReg,
- byte(hashString(indexNameExpr->index.value)) },
- indexNameExpr->indexLocation.begin);
- writeInstruction32(constantIdx,
- indexNameExpr->indexLocation.begin);
- regCount = regBackup;
- }
- void compileGlobalExpr(ScriptParser::AstExprGlobal* globalExpr, byte reg)
- {
- auto it = std::find(modifiedGlobals.begin(), modifiedGlobals.end(),
- std::string{ globalExpr->name.value });
- auto constantIdx = getOrCreateConstant(new (a)
- ConstantString{ globalExpr->name.value, compiler });
- if (it == modifiedGlobals.end())
- {
- auto encodedConstIdx = encodeGlobalConstantIndex(constantIdx);
- auto globalConstIdx = getOrCreateConstant(new (a)
- ConstantGlobal{ encodedConstIdx });
- if (constantIdx < 0x400 && globalConstIdx < 0x8000)
- {
- writeInstruction32({ OpCode::GetGlobalConst, reg,
- uint16_t(globalConstIdx) }, globalExpr->location.begin);
- writeInstruction32(encodedConstIdx,
- globalExpr->location.begin);
- return;
- }
- }
- writeInstruction32({ OpCode::GetGlobal,
- reg, 0, byte(hashString(globalExpr->name.value)) },
- globalExpr->location.begin);
- writeInstruction32(constantIdx,
- globalExpr->location.begin);
- }
- // TODO: rearrange arguments and add defaults
- byte compileCallExpr(ScriptParser::AstExprCall* callExpr,
- byte retBase, byte retCount, bool selfAllocate, bool tailCall)
- {
- auto regBackup = regCount;
- byte funcReg;
- if (selfAllocate)
- {
- funcReg = allocateRegisters(1);
- }
- else
- {
- funcReg = retBase == regCount - 1 ? retBase : allocateRegisters(1);
- }
- byte selfIdxReg = funcReg;
- ScriptParser::AstExprIndexName* idxNameExpr = nullptr;
- if (callExpr->self)
- {
- allocateRegisters(1);
- idxNameExpr = callExpr->func->as<ScriptParser::AstExprIndexName>();
- if (idxNameExpr)
- {
- selfIdxReg = getLocalOrCompileExpression(idxNameExpr->expr, funcReg);
- }
- }
- if (!idxNameExpr)
- compileExpression(callExpr->func, funcReg);
- // TODO: might cause problems still
- // callExpr->self?
- // auto extraReg = std::max<int>(callExpr->args.size - retCount, 0);
- // std::max(int(callExpr->args.size) - retCount, int(callExpr->self));
- // allocateRegisters(extraReg);
- auto argBase = regCount;
- bool wasTail = false;
- for (size_t i = 0; i < callExpr->args.size; ++i)
- {
- auto expr = callExpr->args.data[i];
- if (i + 1 == callExpr->args.size)
- {
- wasTail = compileTailExpression(expr, allocateRegisters(1));
- }
- else
- {
- compileExpression(expr, allocateRegisters(1));
- }
- }
- if (callExpr->self)
- {
- auto constIdx = getOrCreateConstant(new (a) ConstantString
- { idxNameExpr->index.value, compiler });
- writeInstruction32({ OpCode::Self, funcReg, selfIdxReg,
- byte(hashString(idxNameExpr->index.value)) },
- callExpr->location.begin);
- writeInstruction32(constIdx, callExpr->location.begin);
- }
- writeInstruction32({ OpCode::Call, funcReg,
- byte(wasTail ? 0 : callExpr->args.size + callExpr->self + 1),
- byte(tailCall ? 0 : retCount + 1) },
- callExpr->location.begin);
- if (!selfAllocate)
- {
- for (int i = 0; i < retCount; ++i)
- {
- if (retBase + i == funcReg + i)
- continue;
- writeInstruction32({ OpCode::Move,
- byte(retBase + i), byte(funcReg + i), 0 },
- callExpr->location.begin);
- }
- }
- regCount = regBackup;
- if (selfAllocate)
- {
- retBase = allocateRegisters(retCount);
- }
- return retBase;
- }
- void compileRetStat(ScriptParser::AstStatReturn* retStat)
- {
- auto regBackup = regCount;
- /*
- if (retStat->list.size == 1)
- {
- auto reg = getLocalOrCompileExpression(retStat->list.data[0], allocateRegisters(1));
- writeInstruction32(
- { OpCode::Return, reg, 2, 0 },
- retStat->location.begin);
- return;
- }*/
- auto retBaseReg = allocateRegisters(retStat->list.size);
- bool wasTail = false;
- for (size_t i = 0; i < retStat->list.size; ++i)
- {
- auto expr = retStat->list.data[i];
- if (i + 1 == retStat->list.size)
- {
- wasTail = compileTailExpression(expr, retBaseReg + i);
- }
- else
- {
- compileExpression(expr, retBaseReg + i);
- }
- }
- saveUpvalueRegisters(0, retStat->location.begin);
- writeInstruction32(
- { OpCode::Return, retBaseReg, byte(wasTail ? 0 : retStat->list.size + 1), 0 },
- retStat->location.begin);
- regCount = regBackup;
- }
- void compileStatement(ScriptParser::AstStat* stat)
- {
- if (auto blockStat = stat->as<ScriptParser::AstStatBlock>())
- {
- const auto localsBackup = locals;
- const auto regBackup = regCount;
- for (const auto& bodyStat : blockStat->body)
- {
- compileStatement(bodyStat);
- }
- saveUpvalueRegisters(regBackup);
- regCount = regBackup;
- locals = localsBackup;
- }
- else if (auto ifStat = stat->as<ScriptParser::AstStatIf>())
- {
- compileIfStat(ifStat);
- }
- else if (auto whileStat = stat->as<ScriptParser::AstStatWhile>())
- {
- compileWhileStat(whileStat);
- }
- else if (auto repeatStat = stat->as<ScriptParser::AstStatRepeat>())
- {
- compileRepeatStat(repeatStat);
- }
- else if (auto breakStat = stat->as<ScriptParser::AstStatBreak>())
- {
- jumpOutQueue.push_back(code.size());
- writeInstruction32({ OpCode::Jump, 0, int16_t(0) },
- breakStat->location.begin);
- }
- else if (auto retStat = stat->as<ScriptParser::AstStatReturn>())
- {
- compileRetStat(retStat);
- }
- else if (auto exprStat = stat->as<ScriptParser::AstStatExpr>())
- {
- if (auto callExpr = exprStat->expr->as<ScriptParser::AstExprCall>())
- {
- compileCallExpr(callExpr, 0, 0, true, false);
- }
- else
- {
- throw runtime_error("unexpected error compiling AstStatExpr");
- }
- }
- else if (auto localStat = stat->as<ScriptParser::AstStatLocal>())
- {
- compileLocalStat(localStat);
- }
- else if (auto forStat = stat->as<ScriptParser::AstStatFor>())
- {
- compileForStat(forStat);
- }
- else if (auto forInStat = stat->as<ScriptParser::AstStatForIn>())
- {
- compileForInStat(forInStat);
- }
- else if (auto assignStat = stat->as<ScriptParser::AstStatAssign>())
- {
- compileAssignStat(assignStat);
- }
- }
- void compileRepeatStat(ScriptParser::AstStatRepeat* repeatStat)
- {
- auto regBackup = regCount;
- auto jumpOutQueueStart = jumpOutQueue.size();
- auto loopStartIndex = code.size();
- auto localsBackup = locals;
- loopDepth++;
- compileStatement(repeatStat->body);
- loopDepth--;
- std::vector<size_t> jumpIndicies;
- compileConditionExpr(repeatStat->condition, nullptr, jumpIndicies, true);
- auto loopJumpIndex = code.size();
- writeInstruction32({ OpCode::LoopJump, 0, int16_t(0) });
- locals = localsBackup;
- auto loopEndIndex = code.size();
- calculateJumps(jumpIndicies, loopEndIndex);
- calculateJump(loopJumpIndex, loopStartIndex);
- for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
- {
- calculateJump(jumpOutQueue.at(i), loopEndIndex);
- }
- jumpOutQueue.resize(jumpOutQueueStart);
- regCount = regBackup;
- }
- void compileForInStat(ScriptParser::AstStatForIn* forInStat)
- {
- auto jumpOutQueueStart = jumpOutQueue.size();
- auto regBackup = regCount;
- auto assignBase = allocateRegisters(3);
- bool wasTail = false;
- for (size_t i = 0; i < forInStat->values.size; ++i)
- {
- auto expr = forInStat->values.data[i];
- if (i + 1 == forInStat->values.size)
- {
- if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
- {
- compileCallExpr(callExpr, assignBase + i,
- 3 - i, false, false);
- wasTail = true;
- }
- else if (expr->as<ScriptParser::AstExprVarargs>())
- {
- writeInstruction32({ OpCode::LoadVarargs,
- byte(3 - i + 1), 0, 0 },
- expr->location.begin);
- wasTail = true;
- }
- else
- {
- compileExpression(expr, assignBase + i);
- }
- }
- else
- {
- compileExpression(expr, assignBase + i);
- }
- }
- if (!wasTail
- && forInStat->values.size < 3)
- {
- for (auto i = assignBase + forInStat->values.size; i <= assignBase + forInStat->values.size
- + (3 - forInStat->values.size - 1); ++i)
- {
- writeInstruction32({ OpCode::LoadNil, byte(i), 0, 0 },
- forInStat->location.begin);
- }
- }
- auto localsBackup = locals;
- auto varBase = allocateRegisters(2);
- for (size_t i = 0; i < forInStat->vars.size; ++i)
- {
- assignLocal(varBase + i, forInStat->vars.data[i]->name.value);
- }
- auto loopStartIndex = code.size();
- writeInstruction32({ OpCode::Jump, assignBase, int16_t(0) },
- forInStat->location.begin);
- auto bodyStartIndex = code.size();
- loopDepth++;
- compileStatement(forInStat->body);
- loopDepth--;
- saveUpvalueRegisters(regBackup);
- locals = localsBackup;
- auto tForLoopIndex = code.size();
- writeInstruction32({ OpCode::TForLoop, assignBase, int16_t(0) });
- writeInstruction32({ forInStat->vars.size });
- auto loopEndIndex = code.size();
- calculateJump(loopStartIndex, tForLoopIndex);
- calculateJump(tForLoopIndex, bodyStartIndex);
- for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
- {
- calculateJump(jumpOutQueue.at(i), loopEndIndex);
- }
- jumpOutQueue.resize(jumpOutQueueStart);
- regCount = regBackup;
- }
- void compileForStat(ScriptParser::AstStatFor* forStat)
- {
- auto jumpOutQueueStart = jumpOutQueue.size();
- auto regBackup = regCount;
- auto localsBackup = locals;
- auto prepArgBase = allocateRegisters(3);
- assignLocal(prepArgBase + 2, forStat->var->name.value);
- compileExpression(forStat->from, prepArgBase + 2);
- compileExpression(forStat->to, prepArgBase);
- if (forStat->step)
- {
- compileExpression(forStat->step, prepArgBase + 1);
- }
- else
- {
- writeInstruction32({ OpCode::LoadShort, byte(prepArgBase + 1), 1, 0 },
- forStat->to->location.end);
- }
- auto forPrepIndex = code.size();
- writeInstruction32({ OpCode::ForPrep, prepArgBase, int16_t(0) },
- forStat->to->location.begin);
- auto bodyStartIndex = code.size();
- loopDepth++;
- compileStatement(forStat->body);
- loopDepth--;
- saveUpvalueRegisters(regBackup);
- auto forLoopIndex = code.size();
- writeInstruction32({ OpCode::ForLoop, prepArgBase, int16_t(0) },
- forStat->body->location.begin);
- auto loopEndIndex = code.size();
- calculateJump(forPrepIndex, loopEndIndex);
- calculateJump(forLoopIndex, bodyStartIndex);
- for (auto i = jumpOutQueueStart; i < jumpOutQueue.size(); ++i)
- {
- calculateJump(jumpOutQueue.at(i), loopEndIndex);
- }
- jumpOutQueue.resize(jumpOutQueueStart);
- regCount = regBackup;
- locals = localsBackup;
- }
- void compileLocalStat(ScriptParser::AstStatLocal* localStat)
- {
- auto varBase = allocateRegisters(localStat->vars.size);
- if (localStat->usingFunctionSugar)
- {
- for (size_t i = 0; i < localStat->vars.size; ++i)
- {
- std::string localName = localStat->vars.data[i]->name.value;
- assignLocal(varBase + i, localName);
- }
- }
- bool wasTail = false;
- for (size_t i = 0; i < localStat->values.size; ++i)
- {
- auto expr = localStat->values.data[i];
- if (i + 1 == localStat->values.size)
- {
- if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
- {
- compileCallExpr(callExpr, varBase + i,
- localStat->vars.size - i, false, false);
- wasTail = true;
- }
- else if (expr->as<ScriptParser::AstExprVarargs>())
- {
- writeInstruction32({ OpCode::LoadVarargs,
- byte(localStat->vars.size - i + 1), 0, 0 },
- expr->location.begin);
- wasTail = true;
- }
- else
- {
- compileExpression(expr, varBase + i);
- }
- }
- else
- {
- compileExpression(expr, varBase + i);
- }
- }
- if (!localStat->usingFunctionSugar)
- {
- for (size_t i = 0; i < localStat->vars.size; ++i)
- {
- std::string localName = localStat->vars.data[i]->name.value;
- assignLocal(varBase + i, localName);
- }
- }
- if (!wasTail
- && localStat->vars.size != localStat->values.size)
- {
- for (auto i = varBase + localStat->values.size; i <= varBase + localStat->values.size
- + (localStat->vars.size - localStat->values.size - 1); ++i)
- {
- writeInstruction32({ OpCode::LoadNil, byte(i), 0, 0 },
- localStat->location.begin);
- }
- }
- }
- // TODO: remove extra move on locals
- void compileAssignStat(ScriptParser::AstStatAssign* assignStat)
- {
- /*
- if (assignStat->vars.size == 1
- && assignStat->values.size == 1)
- {
- auto assignBase = allocateRegisters(1);
- compileExpression(assignStat->values.data[0], assignBase);
- compileAssignment(assignStat->vars.data[0], assignBase);
- freeRegisters(1);
- return;
- }*/
- auto regBackup = regCount;
- std::unordered_map<ScriptParser::AstExprIndexName*, byte> indexMap;
- for (size_t i = 0; i < assignStat->vars.size; ++i)
- {
- auto expr = assignStat->vars.data[i];
- if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
- {
- byte reg = allocateRegisters(1);
- indexMap[indexNameExpr] = reg;
- compileExpression(indexNameExpr->expr, reg);
- }
- }
- auto assignBase = allocateRegisters(assignStat->vars.size);
- bool wasTail = false;
- for (size_t i = 0; i < assignStat->values.size; ++i)
- {
- auto expr = assignStat->values.data[i];
- if (i + 1 == assignStat->values.size)
- {
- if (auto callExpr = expr->as<ScriptParser::AstExprCall>())
- {
- compileCallExpr(callExpr, assignBase + i,
- assignStat->vars.size - i, false, false);
- wasTail = true;
- }
- else if (expr->as<ScriptParser::AstExprVarargs>())
- {
- writeInstruction32({ OpCode::LoadVarargs,
- byte(assignStat->vars.size - i + 1), 0, 0 },
- expr->location.begin);
- wasTail = true;
- }
- else
- {
- compileExpression(expr, assignBase + i);
- }
- }
- else
- {
- compileExpression(expr, assignBase + i);
- }
- }
- if (!wasTail
- && assignStat->vars.size != assignStat->values.size)
- {
- for (auto i = assignBase + assignStat->values.size; i <= assignBase + assignStat->values.size
- + (assignStat->vars.size - assignStat->values.size - 1); ++i)
- {
- writeInstruction32({ OpCode::LoadNil, byte(i), 0, 0 },
- assignStat->location.begin);
- }
- }
- for (size_t i = 0; i < assignStat->vars.size; ++i)
- {
- auto expr = assignStat->vars.data[i];
- if (auto indexNameExpr = expr->as<ScriptParser::AstExprIndexName>())
- {
- auto idxName = indexNameExpr->index.value;
- writeInstruction32({ OpCode::SetTableIndexConstant, byte(assignBase + i),
- indexMap.at(indexNameExpr), byte(hashString(idxName)) });
- writeInstruction32(getOrCreateConstant(new (a)
- ConstantString{ idxName, compiler }),
- indexNameExpr->indexLocation.begin);
- }
- else
- {
- compileAssignment(expr, assignBase + i);
- }
- }
- regCount = regBackup;
- }
- void compileAssignment(ScriptParser::AstExpr* var, byte reg)
- {
- if (auto localExpr = var->as<ScriptParser::AstExprLocal>())
- {
- compileLocalExpr(localExpr, reg, true);
- }
- else if (auto globalExpr = var->as<ScriptParser::AstExprGlobal>())
- {
- writeInstruction32({ OpCode::SetGlobal,
- reg, 0, byte(hashString(globalExpr->name.value)) },
- globalExpr->location.begin);
- auto constantIdx = getOrCreateConstant(new (a)
- ConstantString{ globalExpr->name.value, compiler });
- writeInstruction32(constantIdx, globalExpr->location.begin);
- modifiedGlobals.emplace_back(globalExpr->name.value);
- }
- else if (auto indexNameExpr = var->as<ScriptParser::AstExprIndexName>())
- {
- auto exprIdxReg = allocateRegisters(1);
- compileExpressionIndexOptimized(indexNameExpr->expr, exprIdxReg);
- auto idxExpr = indexNameExpr->index;
- writeInstruction32({ OpCode::SetTableIndexConstant, reg, exprIdxReg,
- byte(hashString(idxExpr.value)) },
- indexNameExpr->indexLocation.begin);
- writeInstruction32(getOrCreateConstant(new (a)
- ConstantString{ idxExpr.value, compiler }),
- indexNameExpr->indexLocation.begin);
- freeRegisters(1);
- }
- else if (auto indexExpr = var->as<ScriptParser::AstExprIndexExpr>())
- {
- auto exprIdxBase = allocateRegisters(1);
- compileExpressionIndexOptimized(indexExpr->expr, exprIdxBase);
- auto numConstExpr = indexExpr->index->as<ScriptParser::AstExprConstantNumber>();
- if (numConstExpr
- && floor(numConstExpr->value) == numConstExpr->value
- && numConstExpr->value >= 1
- && numConstExpr->value <= UCHAR_MAX + 1)
- {
- writeInstruction32({ OpCode::SetTableIndexByte, reg, exprIdxBase,
- byte(numConstExpr->value - 1) }, numConstExpr->location.begin);
- }
- else
- {
- auto indexValue = allocateRegisters(1);
- compileExpression(indexExpr->index, indexValue);
- writeInstruction32({ OpCode::SetTableIndex, reg, exprIdxBase, indexValue },
- indexExpr->index->location.begin);
- freeRegisters(1);
- }
- freeRegisters(1);
- }
- }
- void saveUpvalueRegisters(int base, const ScriptParser::Position& pos)
- {
- byte last_reg = 255;
- for (auto it = passedUpvals.begin(); it != passedUpvals.end();)
- {
- if (it->second >= base
- && it->second < last_reg)
- {
- last_reg = it->second;
- it = passedUpvals.erase(it);
- writeInstruction32({ OpCode::SaveRegisters, last_reg, 0, 0 }, pos);
- }
- else
- {
- it++;
- }
- }
- }
- void saveUpvalueRegisters(int base)
- {
- byte last_reg = 255;
- for (auto it = passedUpvals.begin(); it != passedUpvals.end();)
- {
- if (it->second >= base
- && it->second < last_reg)
- {
- last_reg = it->second;
- it = passedUpvals.erase(it);
- writeInstruction32({ OpCode::SaveRegisters, last_reg, 0, 0 });
- }
- else
- {
- it++;
- }
- }
- }
- void compileFunction(ScriptParser::AstExprFunction* funcExpr, bool isMain = false)
- {
- bool hasSelf = funcExpr->self != nullptr;
- isVarArg = funcExpr->vararg;
- argCount = byte(hasSelf + funcExpr->args.size);
- writeInstruction32({ OpCode(byte(OpCode::ClearStack) + funcExpr->vararg),
- byte(hasSelf + funcExpr->args.size), 0, 0 },
- funcExpr->location.begin);
- const byte argBaseIndex = allocateRegisters(hasSelf + funcExpr->args.size);
- if (hasSelf)
- {
- assignLocal(argBaseIndex, funcExpr->self->name.value);
- }
- for (size_t i = 0; i < funcExpr->args.size; ++i)
- {
- const auto local = funcExpr->args.data[i];
- assignLocal(argBaseIndex + hasSelf + i, local->name.value);
- }
- if (auto block = funcExpr->body->as<ScriptParser::AstStatBlock>())
- {
- for (const auto& stat : block->body)
- {
- compileStatement(stat);
- }
- if (!ParserUtils::statReturns(block))
- {
- saveUpvalueRegisters(0, block->location.end);
- writeInstruction32({ OpCode::Return, 0, 1, 0 },
- funcExpr->location.end);
- }
- }
- }
- };
- tsl::ordered_map<ScriptParser::AstExprFunction*, FunctionPrototype> protos;
- public:
- Compiler(bool debugInfo = false)
- : debugInfo(debugInfo) {}
- void compile(ScriptParser::AstStat* root, ScriptParser::AstNameTable& names,
- ScriptParser::Allocator& a)
- {
- root->visit(&assignmentVisitor);
- root->visit(&functionVisitor);
- for (auto funcExpr : funcExprs)
- {
- FunctionPrototype p{ a, *this };
- if (funcExpr.second)
- {
- funcExpr.second->visit(&constantVisitor);
- if (auto localStat = funcExpr.second->as<ScriptParser::AstStatLocal>())
- {
- p.name = localStat->vars.data[0]->name.value;
- auto nameIt = std::find(strings.cbegin(),
- strings.cend(), p.name.value());
- if (nameIt == strings.cend())
- {
- strings.push_back(p.name.value());
- }
- }
- else if (auto assignStat =
- funcExpr.second->as<ScriptParser::AstStatAssign>())
- {
- auto global =
- assignStat->vars.data[0]->as<ScriptParser::AstExprGlobal>();
- if (global)
- {
- p.name = global->name.value;
- }
- }
- }
- else
- {
- funcExpr.first->visit(&constantVisitor);
- }
- p.compileFunction(funcExpr.first);
- protos.insert({ funcExpr.first, p });
- }
- if (auto block = root->as<ScriptParser::AstStatBlock>())
- {
- auto funcExpr = new (a) ScriptParser::AstExprFunction{
- block->location, nullptr, {}, true, block
- };
- FunctionPrototype p{ a, *this };
- p.compileFunction(funcExpr, true);
- protos.insert({ funcExpr, p });
- }
- }
- void serialize(ByteStream& buffer)
- {
- buffer << true; // successful compilation
- buffer << int(strings.size());
- for (const auto& str : strings)
- {
- buffer << int(str.size());
- buffer << str;
- }
- buffer << int(protos.size());
- for (const auto& protoEntry : protos)
- {
- auto& proto = protoEntry.second;
- buffer << proto.maxRegCount
- << proto.argCount
- << byte(proto.upvals.size())
- << proto.isVarArg;
- buffer << int(proto.code.size());
- #ifdef _DEBUG
- {
- for (auto it = proto.code.begin(); it != proto.code.end(); ++it)
- {
- uint32_t instrData = *it;
- auto instr = Instruction32{ instrData };
- bool wideInstruction = false;
- switch (instr.op)
- {
- case OpCode::GetGlobal:
- case OpCode::SetGlobal:
- case OpCode::GetGlobalConst:
- case OpCode::GetTableIndexConstant:
- case OpCode::SetTableIndexConstant:
- case OpCode::Self:
- case OpCode::Equal:
- case OpCode::LesserOrEqual:
- case OpCode::LesserThan:
- case OpCode::NotEqual:
- case OpCode::GreaterThan:
- case OpCode::GreaterOrEqual:
- case OpCode::NewTable:
- case OpCode::SetList:
- case OpCode::TForLoop:
- case OpCode::LoadConstLarge:
- wideInstruction = true;
- default:;
- }
- {
- auto encodedBytes = (byte*)&instr.encoded;
- for (int i = 0; i < 4; ++i)
- {
- std::cout << int(encodedBytes[i]) << " ";
- }
- }
- byte clientOp = getClientOp(instr.op);
- instr.op = OpCode(clientOp);
- buffer << instr.encoded;
- if (wideInstruction)
- {
- it++;
- auto encodedBytes = (byte*)&*it;
- for (int i = 0; i < 4; ++i)
- {
- std::cout << int(encodedBytes[i]) << " ";
- }
- buffer << *it;
- }
- else
- {
- std::cout << "\t";
- }
- std::cout << "\t; client: " << int(clientOp) << "\n";
- }
- }
- std::cout << "\n";
- #else
- auto& dataVec = buffer.vec();
- auto initialDataVecSize = dataVec.size();
- auto codeLenBytes = sizeof(uint32_t) * proto.code.size();
- dataVec.resize(dataVec.size() + codeLenBytes);
- memcpy(&dataVec[initialDataVecSize],
- &proto.code[0], codeLenBytes);
- #endif
- buffer << int(proto.constants.size());
- for (const auto& constant : proto.constants)
- {
- buffer << byte(constant->getType());
- constant->writeData(buffer);
- }
- buffer << int(proto.closures.size());
- for (const auto& funcExpr : proto.closures)
- {
- buffer << int(std::distance(protos.begin(), protos.find(funcExpr)));
- }
- // function name index
- if (proto.name.has_value())
- {
- auto nameString = proto.name.value();
- auto nameIt =
- std::find(strings.cbegin(), strings.cend(), nameString);
- if (nameIt != strings.cend())
- {
- auto nameIndex = std::distance(strings.cbegin(), nameIt);
- buffer << int(nameIndex + 1);
- }
- else
- {
- buffer << int(0);
- }
- }
- else
- {
- buffer << int(0);
- }
- buffer << int(proto.lineInfo.size());
- for (const auto& line : proto.lineInfo)
- {
- if (line > 0 && line < SCHAR_MAX)
- {
- buffer << byte(line);
- }
- else
- {
- buffer << int(line);
- }
- }
- buffer << debugInfo;
- if (debugInfo)
- {
- throw runtime_error("debug info is not supported");
- }
- }
- buffer << int(protos.size() - 1); // main proto index
- }
- };
- void compile(ByteStream& buffer, const std::string& source, bool debugInfo)
- {
- #ifndef _DEBUG
- try
- #endif
- {
- ScriptParser::Allocator a;
- ScriptParser::AstNameTable names{ a };
- ScriptParser::AstStat* root =
- ScriptParser::parse(source.data(), source.size(), names, a);
- Compiler c{ debugInfo };
- c.compile(root, names, a);
- c.serialize(buffer);
- }
- #ifndef _DEBUG
- catch (ScriptParser::ParseError& e)
- {
- buffer << false // unsuccessful compilation
- << (std::stringstream{} << ":" << e.getLocation().begin.line << ": " << e.what()).str();
- }
- catch (std::exception& e)
- {
- buffer.clear(); // incase the exception was encountered mid-serialization
- buffer << false // unsuccessful compilation
- << ":" << std::string(e.what());
- }
- #endif
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement