From 5fe4aca85bbdcf800f1ca541f5136ca3a469c004 Mon Sep 17 00:00:00 2001 From: writemorecode Date: Tue, 10 Feb 2026 17:47:43 +0100 Subject: [PATCH] Fix: execute object and array bytecode correctly in VM --- src/ast/ControlStatementNode.cpp | 5 +- src/ast/MethodCallNode.cpp | 7 +- src/ast/MethodCallWithoutArgumentsNode.cpp | 6 +- src/bytecode/BytecodeMethod.cpp | 1 + src/bytecode/BytecodeMethod.hpp | 10 +- src/bytecode/BytecodeMethodBlock.cpp | 22 ++ src/bytecode/BytecodeMethodBlock.hpp | 5 + src/bytecode/BytecodeProgram.cpp | 6 +- src/bytecode/BytecodeProgram.hpp | 3 +- src/bytecode/Opcode.hpp | 14 +- src/ir/CFG.cpp | 23 +- src/ir/Tac.cpp | 20 +- src/ir/Tac.hpp | 11 +- src/vm/vm.cpp | 231 +++++++++++++++++++-- 14 files changed, 310 insertions(+), 54 deletions(-) diff --git a/src/ast/ControlStatementNode.cpp b/src/ast/ControlStatementNode.cpp index 98702e2..6f11051 100644 --- a/src/ast/ControlStatementNode.cpp +++ b/src/ast/ControlStatementNode.cpp @@ -69,6 +69,7 @@ Operand WhileNode::generateIR(CFG &graph, SymbolTable &st) { graph.setCurrentBlock(headerBlock); const auto &condName = cond->generateIR(graph, st); + auto *conditionBlock = graph.getCurrentBlock(); graph.addInstruction(new CondJumpTac(joinBlock->getName(), condName)); graph.addInstruction(new JumpTac(bodyBlock->getName())); @@ -77,8 +78,8 @@ Operand WhileNode::generateIR(CFG &graph, SymbolTable &st) { graph.getCurrentBlock()->setTrueBlock(headerBlock); graph.addInstruction(new JumpTac(headerLabel)); - headerBlock->setTrueBlock(bodyBlock); - headerBlock->setFalseBlock(joinBlock); + conditionBlock->setTrueBlock(bodyBlock); + conditionBlock->setFalseBlock(joinBlock); graph.setCurrentBlock(joinBlock); diff --git a/src/ast/MethodCallNode.cpp b/src/ast/MethodCallNode.cpp index 9a7b357..dae74f0 100644 --- a/src/ast/MethodCallNode.cpp +++ b/src/ast/MethodCallNode.cpp @@ -23,7 +23,7 @@ Operand MethodCallNode::generateIR(CFG &graph, SymbolTable &st) { } const auto &methodType = method->getType(); - object->generateIR(graph, st); + const auto receiver = object->generateIR(graph, st); for (const auto &arg : exprList->children) { const auto &argName = arg->generateIR(graph, st); @@ -34,8 +34,9 @@ Operand MethodCallNode::generateIR(CFG &graph, SymbolTable &st) { st.addVariable(methodType, name); const auto &methodName = id->value; - const auto argCount = std::to_string(exprList->children.size()); + const auto methodTarget = *caller_type + "." + methodName; + const auto argCount = static_cast(exprList->children.size()); graph.addInstruction( - new MethodCallTac(name, methodName, *caller_type, argCount)); + new MethodCallTac(name, receiver, methodTarget, argCount)); return name; } diff --git a/src/ast/MethodCallWithoutArgumentsNode.cpp b/src/ast/MethodCallWithoutArgumentsNode.cpp index ad7eb0e..a7ce897 100644 --- a/src/ast/MethodCallWithoutArgumentsNode.cpp +++ b/src/ast/MethodCallWithoutArgumentsNode.cpp @@ -21,12 +21,12 @@ Operand MethodCallWithoutArgumentsNode::generateIR(CFG &graph, } const auto &methodType = method->getType(); - object->generateIR(graph, st); + const auto receiver = object->generateIR(graph, st); const auto &name = graph.getTemporaryName(); st.addVariable(methodType, name); const auto &methodName = id->value; - graph.addInstruction( - new MethodCallTac(name, methodName, *caller_type, "0")); + const auto methodTarget = *caller_type + "." + methodName; + graph.addInstruction(new MethodCallTac(name, receiver, methodTarget, 0)); return name; } diff --git a/src/bytecode/BytecodeMethod.cpp b/src/bytecode/BytecodeMethod.cpp index d881157..13d4d91 100644 --- a/src/bytecode/BytecodeMethod.cpp +++ b/src/bytecode/BytecodeMethod.cpp @@ -26,6 +26,7 @@ void BytecodeMethod::print(std::ostream &os) const { void BytecodeMethod::serialize(Serializer &serializer) const { serializer.writeString(name); serializer.writeStringVector(variables); + serializer.writeStringVector(fieldVariables); serializer.writeInteger(blocks.size()); for (const auto &block : blocks) { serializer.writeString(block.getName()); diff --git a/src/bytecode/BytecodeMethod.hpp b/src/bytecode/BytecodeMethod.hpp index 7186835..5a65632 100644 --- a/src/bytecode/BytecodeMethod.hpp +++ b/src/bytecode/BytecodeMethod.hpp @@ -11,11 +11,14 @@ class BytecodeMethod { std::string name; std::vector variables; + std::vector fieldVariables; public: BytecodeMethod(const std::string &name_, - std::vector variables_) - : name(name_), variables(std::move(variables_)) {}; + std::vector variables_, + std::vector fieldVariables_) + : name(name_), variables(std::move(variables_)), + fieldVariables(std::move(fieldVariables_)) {}; bool operator==(const std::string &otherName) const { return name == otherName; @@ -32,6 +35,9 @@ class BytecodeMethod { void print(std::ostream &os) const; [[nodiscard]] const auto &getVariables() const { return variables; } + [[nodiscard]] const auto &getFieldVariables() const { + return fieldVariables; + } void serialize(Serializer &serializer) const; }; diff --git a/src/bytecode/BytecodeMethodBlock.cpp b/src/bytecode/BytecodeMethodBlock.cpp index 0501496..baf12c8 100644 --- a/src/bytecode/BytecodeMethodBlock.cpp +++ b/src/bytecode/BytecodeMethodBlock.cpp @@ -88,6 +88,28 @@ BytecodeMethodBlock &BytecodeMethodBlock::call(const std::string &method) { new StringParameterInstruction(Opcode::CALL, method)); return *this; } +BytecodeMethodBlock & +BytecodeMethodBlock::new_object(const std::string &className) { + addBytecodeInstruction( + new StringParameterInstruction(Opcode::NEW, className)); + return *this; +} +BytecodeMethodBlock &BytecodeMethodBlock::new_array() { + addBytecodeInstruction(new StackParameterInstruction(Opcode::NEW_ARRAY)); + return *this; +} +BytecodeMethodBlock &BytecodeMethodBlock::array_load() { + addBytecodeInstruction(new StackParameterInstruction(Opcode::ARRAY_LOAD)); + return *this; +} +BytecodeMethodBlock &BytecodeMethodBlock::array_store() { + addBytecodeInstruction(new StackParameterInstruction(Opcode::ARRAY_STORE)); + return *this; +} +BytecodeMethodBlock &BytecodeMethodBlock::array_length() { + addBytecodeInstruction(new StackParameterInstruction(Opcode::ARRAY_LENGTH)); + return *this; +} BytecodeMethodBlock &BytecodeMethodBlock::jump(const std::string &location) { addBytecodeInstruction( diff --git a/src/bytecode/BytecodeMethodBlock.hpp b/src/bytecode/BytecodeMethodBlock.hpp index 7af593e..23f7362 100644 --- a/src/bytecode/BytecodeMethodBlock.hpp +++ b/src/bytecode/BytecodeMethodBlock.hpp @@ -39,6 +39,11 @@ class BytecodeMethodBlock { BytecodeMethodBlock &write(); BytecodeMethodBlock &call(const std::string &method); + BytecodeMethodBlock &new_object(const std::string &className); + BytecodeMethodBlock &new_array(); + BytecodeMethodBlock &array_load(); + BytecodeMethodBlock &array_store(); + BytecodeMethodBlock &array_length(); BytecodeMethodBlock &jump(const std::string &location); BytecodeMethodBlock &cjump(const std::string &location); diff --git a/src/bytecode/BytecodeProgram.cpp b/src/bytecode/BytecodeProgram.cpp index 7f5c8ac..5417760 100644 --- a/src/bytecode/BytecodeProgram.cpp +++ b/src/bytecode/BytecodeProgram.cpp @@ -11,8 +11,10 @@ BytecodeMethod & BytecodeProgram::addBytecodeMethod(const std::string &name, - std::vector variables) { - methods.push_back(BytecodeMethod(name, std::move(variables))); + std::vector variables, + std::vector fieldVariables) { + methods.push_back( + BytecodeMethod(name, std::move(variables), std::move(fieldVariables))); return methods.back(); } diff --git a/src/bytecode/BytecodeProgram.hpp b/src/bytecode/BytecodeProgram.hpp index ed8ab48..38c351e 100644 --- a/src/bytecode/BytecodeProgram.hpp +++ b/src/bytecode/BytecodeProgram.hpp @@ -12,7 +12,8 @@ class BytecodeProgram { public: [[nodiscard]] BytecodeMethod & addBytecodeMethod(const std::string &name, - std::vector variables); + std::vector variables, + std::vector fieldVariables); [[nodiscard]] BytecodeMethod &getBytecodeMethod(const std::string &name); diff --git a/src/bytecode/Opcode.hpp b/src/bytecode/Opcode.hpp index af17f0b..2e48053 100644 --- a/src/bytecode/Opcode.hpp +++ b/src/bytecode/Opcode.hpp @@ -22,11 +22,17 @@ enum Opcode : int8_t { CALL = 15, RET = 16, PRINT = 17, - STOP = 18 + STOP = 18, + NEW = 19, + NEW_ARRAY = 20, + ARRAY_LOAD = 21, + ARRAY_STORE = 22, + ARRAY_LENGTH = 23 }; const std::vector mnemonics{ - "ILOAD", "ICONST", "ISTORE", "IADD", "ISUB", "IMUL", "IDIV", - "ILT", "IGT", "IEQ", "IAND", "IOR", "INOT", "GOTO", - "IFFALSE GOTO", "INVOKEVIRTUAL", "IRETURN", "PRINT", "STOP"}; + "ILOAD", "ICONST", "ISTORE", "IADD", "ISUB", "IMUL", + "IDIV", "ILT", "IGT", "IEQ", "IAND", "IOR", + "INOT", "GOTO", "IFFALSE GOTO", "INVOKEVIRTUAL", "IRETURN", "PRINT", + "STOP", "NEW", "NEWARRAY", "IALOAD", "IASTORE", "IALEN"}; #endif diff --git a/src/ir/CFG.cpp b/src/ir/CFG.cpp index ff9679e..09c62be 100644 --- a/src/ir/CFG.cpp +++ b/src/ir/CFG.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -96,23 +97,27 @@ void CFG::generateBytecode(BytecodeProgram &program, SymbolTable &st) { auto *methodScope = st.resolveScope(className, methodName); const auto *method = dynamic_cast(methodScope->getRecord()); + const auto *classScope = methodScope->getParent(); const auto methodParameters = method->getParameterNames(); const auto &blockName = basicBlock->getName(); - auto variableNames = methodScope->getVariableNames(); - if (const auto *classScope = methodScope->getParent(); - classScope != nullptr) { - const auto classVariableNames = classScope->getVariableNames(); - variableNames.insert(classVariableNames.begin(), - classVariableNames.end()); - } + const auto variableNames = methodScope->getVariableNames(); + auto fieldVariableNames = classScope != nullptr + ? classScope->getVariableNames() + : std::set{}; std::vector variables(variableNames.begin(), variableNames.end()); - auto &bytecodeMethod = - program.addBytecodeMethod(blockName, std::move(variables)); + std::vector fieldVariables(fieldVariableNames.begin(), + fieldVariableNames.end()); + auto &bytecodeMethod = program.addBytecodeMethod( + blockName, std::move(variables), std::move(fieldVariables)); auto &bytecodeBlock = bytecodeMethod.addBytecodeMethodBlock(blockName); + if (basicBlock != mainRoot) { + bytecodeBlock.store("this"); + } + std::for_each(methodParameters.rbegin(), methodParameters.rend(), [&bytecodeBlock](const auto ¶m) { bytecodeBlock.store(param); diff --git a/src/ir/Tac.cpp b/src/ir/Tac.cpp index fc55945..7e95036 100644 --- a/src/ir/Tac.cpp +++ b/src/ir/Tac.cpp @@ -22,22 +22,37 @@ void ArrayCopyTac::print(std::ostream &os) const { os << result << "[" << lhs << "]" << " := " << rhs << "\n"; } +void ArrayCopyTac::generateBytecode(BytecodeMethodBlock &block) { + block.push(result).push(lhsOp).push(rhsOp).array_store(); +} void ArrayAccessTac::print(std::ostream &os) const { os << result << " := " << lhs << "[" << rhs << "]\n"; } +void ArrayAccessTac::generateBytecode(BytecodeMethodBlock &block) { + block.push(lhsOp).push(rhsOp).array_load().store(result); +} void ArrayLengthTac::print(std::ostream &os) const { os << result << " := length " << rhs << "\n"; } +void ArrayLengthTac::generateBytecode(BytecodeMethodBlock &block) { + block.push(rhsOp).array_length().store(result); +} void NewTac::print(std::ostream &os) const { os << result << " := new " << rhs << "\n"; } +void NewTac::generateBytecode(BytecodeMethodBlock &block) { + block.new_object(rhs).store(result); +} void NewArrayTac::print(std::ostream &os) const { os << result << " := new int, " << rhs << "\n"; } +void NewArrayTac::generateBytecode(BytecodeMethodBlock &block) { + block.push(rhsOp).new_array().store(result); +} void JumpTac::print(std::ostream &os) const { os << "goto " << result << "\n"; } void JumpTac::generateBytecode(BytecodeMethodBlock &block) { @@ -52,10 +67,11 @@ void CondJumpTac::generateBytecode(BytecodeMethodBlock &block) { } void MethodCallTac::print(std::ostream &os) const { - os << result << " := call " << lhs << ", " << rhs << "\n"; + os << result << " := call " << op << " on " << lhs << ", " << rhs + << " args\n"; } void MethodCallTac::generateBytecode(BytecodeMethodBlock &block) { - block.call(lhs + "." + op).store(result); + block.push(lhsOp).call(op).store(result); } void ParamTac::print(std::ostream &os) const { os << "param " << rhs << "\n"; } diff --git a/src/ir/Tac.hpp b/src/ir/Tac.hpp index 2f4c80d..1a78cd6 100644 --- a/src/ir/Tac.hpp +++ b/src/ir/Tac.hpp @@ -50,6 +50,7 @@ class ArrayCopyTac : public Tac { const Operand &z_) : Tac(result_, index_, ":=", z_) {}; void print(std::ostream &os) const override; + void generateBytecode(BytecodeMethodBlock &block) override; }; class ArrayAccessTac : public Tac { public: @@ -57,17 +58,20 @@ class ArrayAccessTac : public Tac { const Operand &z_) : Tac(result_, y_, "", z_) {}; void print(std::ostream &os) const override; + void generateBytecode(BytecodeMethodBlock &block) override; }; class ArrayLengthTac : public Tac { public: ArrayLengthTac(const std::string &result, const Operand &y_) : Tac(result, y_) {}; void print(std::ostream &os) const override; + void generateBytecode(BytecodeMethodBlock &block) override; }; class NewTac : public Tac { public: NewTac(const std::string &result, const Operand &y_) : Tac(result, y_) {}; void print(std::ostream &os) const override; + void generateBytecode(BytecodeMethodBlock &block) override; }; class NewArrayTac : public Tac { @@ -75,6 +79,7 @@ class NewArrayTac : public Tac { NewArrayTac(const std::string &result, const Operand &length_) : Tac(result, length_) {}; void print(std::ostream &os) const override; + void generateBytecode(BytecodeMethodBlock &block) override; }; class NotTac : public Tac { @@ -102,9 +107,9 @@ class CondJumpTac : public Tac { class MethodCallTac : public Tac { public: - MethodCallTac(const std::string &result, const std::string &objectName, - const Operand &methodName, const Operand &argCount) - : Tac(result, methodName, objectName, argCount) {}; + MethodCallTac(const std::string &result, const Operand &receiver, + const std::string &methodTarget, const Operand &argCount) + : Tac(result, receiver, methodTarget, argCount) {}; void print(std::ostream &os) const override; void generateBytecode(BytecodeMethodBlock &block) override; }; diff --git a/src/vm/vm.cpp b/src/vm/vm.cpp index c721425..e47213e 100644 --- a/src/vm/vm.cpp +++ b/src/vm/vm.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -6,6 +8,7 @@ #include #include #include +#include #include #include "bytecode/Opcode.hpp" @@ -19,6 +22,17 @@ struct Instruction { : op(op_), argNumber(argNum_), argString(argStr_) {}; }; +[[nodiscard]] std::string +class_name_from_method_name(const std::string &methodName) { + const auto separator = methodName.find('.'); + if (separator == std::string::npos) { + return methodName; + } + return methodName.substr(0, separator); +} + +using Value = std::int64_t; + struct Block { std::vector instructions; void print() const { @@ -34,7 +48,8 @@ struct Block { }; struct Method { - std::vector variables; + std::vector localVariables; + std::vector fieldVariables; std::unordered_map blocks; void print() const { @@ -48,32 +63,56 @@ struct Method { class Activation { size_t pc = 0; Method method; - std::unordered_map variables; + std::unordered_map localVariables; + std::unordered_set fieldVariables; std::string currentBlock; + std::string className; + Value thisReference = 0; public: explicit Activation(const Method &method_, const std::string ¤tBlock_) - : method(method_), currentBlock(currentBlock_) { - for (const auto &name : method.variables) { - variables[name] = 0; + : method(method_), fieldVariables(method_.fieldVariables.begin(), + method_.fieldVariables.end()), + currentBlock(currentBlock_), + className(class_name_from_method_name(currentBlock_)) { + for (const auto &name : method.localVariables) { + localVariables[name] = 0; } }; auto getPC() const { return pc; } auto getCurrentBlockName() const { return currentBlock; } + [[nodiscard]] const auto &getClassName() const { return className; } const auto &step() { - const auto &block = method.blocks[currentBlock]; + const auto it = method.blocks.find(currentBlock); + if (it == method.blocks.end()) { + throw std::invalid_argument("block " + currentBlock + + " not found"); + } + const auto &block = it->second; + if (pc >= block.instructions.size()) { + throw std::out_of_range("instruction pointer out of bounds in " + + currentBlock); + } return block.instructions[pc++]; } - void setVariable(const std::string &name, size_t value) { - variables[name] = value; + [[nodiscard]] bool hasLocalVariable(const std::string &name) const { + return localVariables.contains(name); } - [[nodiscard]] size_t getVariable(const std::string &name) const { - if (const auto &it = variables.find(name); it != variables.end()) { + [[nodiscard]] bool hasFieldVariable(const std::string &name) const { + return fieldVariables.contains(name); + } + [[nodiscard]] Value getLocalVariable(const std::string &name) const { + if (const auto &it = localVariables.find(name); + it != localVariables.end()) { return it->second; } - // FIXME: Find a way to remove this exception throw std::invalid_argument("variable " + name + " not found"); } + void setLocalVariable(const std::string &name, Value value) { + localVariables[name] = value; + } + [[nodiscard]] Value getThisReference() const { return thisReference; } + void setThisReference(Value value) { thisReference = value; } void setCurrentBlock(const std::string &blockName) { currentBlock = blockName; pc = 0; @@ -83,6 +122,19 @@ class Program { std::string mainMethodName; Method mainMethod; std::unordered_map methods; + std::unordered_map> classFieldNames; + + void registerMethodFields(const std::string &methodName, + const Method &method) { + const auto className = class_name_from_method_name(methodName); + auto &fields = classFieldNames[className]; + for (const auto &fieldName : method.fieldVariables) { + if (std::find(fields.begin(), fields.end(), fieldName) == + fields.end()) { + fields.push_back(fieldName); + } + } + } public: const auto &getMain() const { return mainMethod; } @@ -98,10 +150,24 @@ class Program { Program(const std::string &mainMethodName_, const Method &mainMethod_, const std::unordered_map &methods_) : mainMethodName(mainMethodName_), mainMethod(mainMethod_), - methods(methods_) {}; + methods(methods_) { + registerMethodFields(mainMethodName, mainMethod); + for (const auto &[methodName, method] : methods) { + registerMethodFields(methodName, method); + } + }; void print() const; std::string getMainMethodName() const { return mainMethodName; } + [[nodiscard]] const std::vector & + getClassFieldNames(const std::string &className) const { + static const std::vector empty; + if (const auto &it = classFieldNames.find(className); + it != classFieldNames.end()) { + return it->second; + } + return empty; + } }; void Program::print() const { @@ -114,11 +180,18 @@ void Program::print() const { } class VM { - std::stack dataStack; + struct ObjectInstance { + std::string className; + std::unordered_map fields; + }; + + std::stack dataStack; std::stack> activations; std::shared_ptr currentActivation; Program program; + std::vector objects; + std::vector> arrays; Instruction step() { if (currentActivation == nullptr) { @@ -127,16 +200,86 @@ class VM { } return currentActivation->step(); } - void setVariableValue(const std::string &name, size_t value) { - currentActivation->setVariable(name, value); + + [[nodiscard]] ObjectInstance &getObjectByReference(Value reference) { + if (reference <= 0 || static_cast(reference) > objects.size()) { + throw std::invalid_argument("invalid object reference"); + } + return objects[static_cast(reference - 1)]; + } + [[nodiscard]] const ObjectInstance & + getObjectByReference(Value reference) const { + if (reference <= 0 || static_cast(reference) > objects.size()) { + throw std::invalid_argument("invalid object reference"); + } + return objects[static_cast(reference - 1)]; + } + + [[nodiscard]] std::vector &getArrayByReference(Value reference) { + if (reference <= 0 || static_cast(reference) > arrays.size()) { + throw std::invalid_argument("invalid array reference"); + } + return arrays[static_cast(reference - 1)]; + } + + [[nodiscard]] Value allocateObject(const std::string &className) { + ObjectInstance object{.className = className, .fields = {}}; + for (const auto &fieldName : program.getClassFieldNames(className)) { + if (fieldName == "this") { + continue; + } + object.fields[fieldName] = 0; + } + + objects.push_back(std::move(object)); + return static_cast(objects.size()); + } + + void setVariableValue(const std::string &name, Value value) { + if (currentActivation->hasLocalVariable(name)) { + currentActivation->setLocalVariable(name, value); + return; + } + if (currentActivation->hasFieldVariable(name)) { + if (name == "this") { + currentActivation->setThisReference(value); + return; + } + const auto thisReference = currentActivation->getThisReference(); + if (thisReference == 0) { + throw std::invalid_argument("this not initialized"); + } + auto &object = getObjectByReference(thisReference); + object.fields[name] = value; + return; + } + throw std::invalid_argument("variable " + name + " not found"); } - size_t getVariableValue(const std::string &name) const { - return currentActivation->getVariable(name); + [[nodiscard]] Value getVariableValue(const std::string &name) { + if (currentActivation->hasLocalVariable(name)) { + return currentActivation->getLocalVariable(name); + } + if (currentActivation->hasFieldVariable(name)) { + if (name == "this") { + return currentActivation->getThisReference(); + } + const auto thisReference = currentActivation->getThisReference(); + if (thisReference == 0) { + throw std::invalid_argument("this not initialized"); + } + const auto &object = getObjectByReference(thisReference); + if (const auto &it = object.fields.find(name); + it != object.fields.end()) { + return it->second; + } + throw std::invalid_argument("field " + name + " not found"); + } + throw std::invalid_argument("variable " + name + " not found"); } - void push(size_t value) { dataStack.push(value); } + void push(Value value) { dataStack.push(value); } void push(const std::string &name) { push(getVariableValue(name)); } - size_t pop() { + Value pop() { if (dataStack.empty()) { throw std::runtime_error("empty data stack"); } @@ -212,6 +355,46 @@ void VM::run() { std::cout << value << "\n"; break; } + case Opcode::NEW: { + push(allocateObject(instruction.argString)); + break; + } + case Opcode::NEW_ARRAY: { + const auto length = pop(); + if (length < 0) { + throw std::invalid_argument("negative array length"); + } + arrays.emplace_back(length, 0); + push(static_cast(arrays.size())); + break; + } + case Opcode::ARRAY_LOAD: { + const auto index = pop(); + const auto arrayReference = pop(); + const auto &array = getArrayByReference(arrayReference); + if (index < 0 || static_cast(index) >= array.size()) { + throw std::invalid_argument("array index out of bounds"); + } + push(array[static_cast(index)]); + break; + } + case Opcode::ARRAY_STORE: { + const auto value = pop(); + const auto index = pop(); + const auto arrayReference = pop(); + auto &array = getArrayByReference(arrayReference); + if (index < 0 || static_cast(index) >= array.size()) { + throw std::invalid_argument("array index out of bounds"); + } + array[static_cast(index)] = value; + break; + } + case Opcode::ARRAY_LENGTH: { + const auto arrayReference = pop(); + const auto &array = getArrayByReference(arrayReference); + push(array.size()); + break; + } case Opcode::ADD: { auto x = pop(); auto y = pop(); @@ -272,7 +455,7 @@ void VM::run() { break; } case Opcode::CONST: { - push(instruction.argNumber); + push(static_cast(instruction.argNumber)); break; } case Opcode::LOAD: { @@ -300,7 +483,8 @@ void VM::run() { case Opcode::CJMP: case Opcode::CALL: case Opcode::LOAD: - case Opcode::STORE: { + case Opcode::STORE: + case Opcode::NEW: { argString = reader.readString(); break; } @@ -328,7 +512,8 @@ void VM::run() { } [[nodiscard]] Method readMethod(Deserializer &reader) { - const auto variableNames = reader.readStringVector(); + const auto localVariableNames = reader.readStringVector(); + const auto fieldVariableNames = reader.readStringVector(); const auto blockCount = reader.readInteger(); // std::cout << "Block count: " << blockCount << "\n"; @@ -339,7 +524,7 @@ void VM::run() { blocks.emplace(blockName, readBlock(reader)); } - return {variableNames, blocks}; + return {localVariableNames, fieldVariableNames, blocks}; } [[nodiscard]] Program readProgram(Deserializer &reader) {