bool VariableRef::readFloat( float& out ) const { VALIDATE(); pushValue(); out = (float)lua_tonumber(m_ownerContext->getPimpl()->L, -1); popValue(); return true; }
bool VariableRef::readUInt32( uint32& out ) const { VALIDATE(); pushValue(); out = (uint32)lua_tonumber(m_ownerContext->getPimpl()->L, -1); popValue(); return true; }
void LuaModule::popJsonFromArbStack(Json::Value &json, lua_State * state) { lua_pushnil(state); /* first key */ if (!lua_istable(state, -2)) { throw std::runtime_error("Not a table at the top of the stack"); } while (lua_next(state, -2) != 0) { int keyType = lua_type(state, -2); int valType = lua_type(state, -1); /*std::cout << lua_typename(state, keyType) << " - " << lua_typename(state, valType) << std::endl;*/ int keyInt = -100; std::string keyStr = "__INIT_FAILED__"; if (keyType == LUA_TNUMBER) { keyInt = (int)lua_tointeger(state, -2) - 1; } else if (keyType == LUA_TSTRING) { keyStr = lua_tostring(state, -2); } else { throw std::runtime_error("Wrong key type"); } if (valType == LUA_TTABLE) { if (keyType == LUA_TNUMBER) { popJsonFromArbStack(json[keyInt], state); lua_pop(state, 1); } else { popJsonFromArbStack(json[keyStr], state); lua_pop(state, 1); } } else { if (keyType == LUA_TNUMBER) { popValue(json[keyInt], state); } else { popValue(json[keyStr], state); } } } }
bool VariableRef::readBytes( size_t num, uint8* out_buf ) const { VALIDATE(); lua_State* L = m_ownerContext->getPimpl()->L; bool success = false; pushValue(); if (lua_istable(L, -1) == 1) { success = true; lua_pushnil(L); for (size_t i = 0; i < num; ++i) { const int next_ret = lua_next(L, -2); if (next_ret == 0) { log_error("Not enough values in table (%d required, has %d)", num, i); success = false; break; } if (lua_isnumber(L, -1) == 0) { log_error("Non-number value in table when reading bytes"); success = false; break; } // Checks for fractional parts, too high values etc... const uint8 val = (uint8)lua_tonumber(L, -1); out_buf[i] = val; lua_pop(L, 1); } lua_pop(L, 1); } popValue(); return true; }
bool VariableRef::readString( mkString& out ) const { VALIDATE(); bool success = false; pushValue(); const char* ptr = lua_tolstring(m_ownerContext->getPimpl()->L, -1, NULL); if (ptr) { success = true; out = ptr; } popValue(); return success; }
void BytecodeInterpreter::interpret() { while (bci() < bc()->length()) { bool jmp = false; Value first; Value second; switch (bc()->getInsn(bci())) { case BC_STOP: return; case BC_DLOAD0: pushValue(0.0); break; case BC_ILOAD0: pushValue<int64_t>(0); break; case BC_DLOAD1: pushValue(1.0); break; case BC_ILOAD1: pushValue<int64_t>(1); break; case BC_DLOADM1: pushValue(-1.0); break; case BC_ILOADM1: pushValue<int64_t>(-1); break; case BC_DLOAD: pushValue(bc()->getDouble(bci() + 1)); break; case BC_ILOAD: pushValue(bc()->getInt64(bci() + 1)); break; case BC_SLOAD: pushValue(m_code->constantById(bc()->getUInt16(bci() + 1)).c_str()); break; case BC_CALLNATIVE: pushValue(callNativeFunction(bc()->getUInt16(bci() + 1))); break; case BC_CALL: m_locals.push(bc()->getUInt16(bci() + 1)); pushFunc(bc()->getUInt16(bci() + 1)); pushBci(); jmp = true; break; case BC_RETURN: m_locals.pop(); popFunc(); popBci(); //jmp = true; break; #define LOAD_VAR_N(type, n) \ pushValue(m_locals.load(1, n).type()); \ break; case BC_LOADDVAR0: LOAD_VAR_N(doubleValue, 0) case BC_LOADDVAR1: LOAD_VAR_N(doubleValue, 1) case BC_LOADDVAR2: LOAD_VAR_N(doubleValue, 2) case BC_LOADDVAR3: LOAD_VAR_N(doubleValue, 3) case BC_LOADIVAR0: LOAD_VAR_N(intValue, 0) case BC_LOADIVAR1: LOAD_VAR_N(intValue, 1) case BC_LOADIVAR2: LOAD_VAR_N(intValue, 2) case BC_LOADIVAR3: LOAD_VAR_N(intValue, 3) case BC_LOADSVAR0: LOAD_VAR_N(stringValue, 0) case BC_LOADSVAR1: LOAD_VAR_N(stringValue, 1) case BC_LOADSVAR2: LOAD_VAR_N(stringValue, 2) case BC_LOADSVAR3: LOAD_VAR_N(stringValue, 3) #undef LOAD_VAR_N #define STORE_VAR_N(type, n) \ pushValue(m_locals.load(1, n).type()); \ break; case BC_STOREDVAR0: STORE_VAR_N(doubleValue, 0) case BC_STOREDVAR1: STORE_VAR_N(doubleValue, 1) case BC_STOREDVAR2: STORE_VAR_N(doubleValue, 2) case BC_STOREDVAR3: STORE_VAR_N(doubleValue, 3) case BC_STOREIVAR0: STORE_VAR_N(intValue, 0) case BC_STOREIVAR1: STORE_VAR_N(intValue, 1) case BC_STOREIVAR2: STORE_VAR_N(intValue, 2) case BC_STOREIVAR3: STORE_VAR_N(intValue, 3) case BC_STORESVAR0: STORE_VAR_N(stringValue, 0) case BC_STORESVAR1: STORE_VAR_N(stringValue, 1) case BC_STORESVAR2: STORE_VAR_N(stringValue, 2) case BC_STORESVAR3: STORE_VAR_N(stringValue, 3) #undef STORE_VAR_N #define LOAD_VAR(type) \ pushValue(m_locals.load(1, bc()->getUInt16(bci() + 1)).type()); \ break; case BC_LOADDVAR: LOAD_VAR(doubleValue) case BC_LOADIVAR: LOAD_VAR(intValue) case BC_LOADSVAR: LOAD_VAR(stringValue) #undef LOAD_CTX_VAR #define STORE_VAR(type) \ m_locals.store(popValue().type(), 1, bc()->getUInt16(bci() + 1)); \ break; case BC_STOREDVAR: STORE_VAR(doubleValue) case BC_STOREIVAR: STORE_VAR(intValue) case BC_STORESVAR: STORE_VAR(stringValue) #undef STORE_VAR #define LOAD_CTX_VAR(type) \ pushValue(m_locals.load(bc()->getUInt16(bci() + 1), \ bc()->getUInt16(bci() + 3)).type()); \ break; case BC_LOADCTXDVAR: LOAD_CTX_VAR(doubleValue) case BC_LOADCTXIVAR: LOAD_CTX_VAR(intValue) case BC_LOADCTXSVAR: LOAD_CTX_VAR(stringValue) #undef LOAD_CTX_VAR #define STORE_CTX_VAR(type) \ m_locals.store(popValue().type(), \ bc()->getUInt16(bci() + 1), \ bc()->getUInt16(bci() + 3)); \ break; case BC_STORECTXDVAR: STORE_CTX_VAR(doubleValue) case BC_STORECTXIVAR: STORE_CTX_VAR(intValue) case BC_STORECTXSVAR: STORE_CTX_VAR(stringValue) #undef STORE_CTX_VAR #define CMP_JMP(op) \ first = popValue(); \ second = popValue(); \ if (first.intValue() op second.intValue()) { \ bci() += bc()->getInt16(bci() + 1) + 1; \ jmp = true; \ } \ break; case BC_IFICMPNE: CMP_JMP(!=) case BC_IFICMPE: CMP_JMP(==) case BC_IFICMPG: CMP_JMP(>) case BC_IFICMPGE: CMP_JMP(>=) case BC_IFICMPL: CMP_JMP(<) case BC_IFICMPLE: CMP_JMP(<=) #undef CMP_JMP case BC_JA: bci() += bc()->getInt16(bci() + 1) + 1; jmp = true; break; #define BINARY_OP(type, op) \ first = popValue(); \ second = popValue(); \ pushValue(first.type() op second.type()); \ break; case BC_DADD: BINARY_OP(doubleValue, +) case BC_IADD: BINARY_OP(intValue, +) case BC_DSUB: BINARY_OP(doubleValue, -) case BC_ISUB: BINARY_OP(intValue, -) case BC_DMUL: BINARY_OP(doubleValue, *) case BC_IMUL: BINARY_OP(intValue, *) case BC_DDIV: BINARY_OP(doubleValue, /) case BC_IDIV: BINARY_OP(intValue, /) case BC_IMOD: BINARY_OP(intValue, %) case BC_IAOR: BINARY_OP(intValue, |) case BC_IAAND: BINARY_OP(intValue, &) case BC_IAXOR: BINARY_OP(intValue, ^) #undef BINARY_OP #define CMP(type) \ first = popValue(); \ second = popValue(); \ if (first.type() < second.type()) \ pushValue<int64_t>(-1); \ else if (first.type() == second.type()) \ pushValue<int64_t>(0); \ else \ pushValue<int64_t>(1); \ break; case BC_DCMP: CMP(doubleValue) case BC_ICMP: CMP(intValue) #undef CMP case BC_DNEG: pushValue(-popValue().doubleValue()); break; case BC_INEG: pushValue(-popValue().intValue()); break; case BC_S2I: first = popValue(); pushValue<int64_t>(first.stringValue() != 0); break; case BC_I2D: pushValue<double>(popValue().intValue()); break; case BC_D2I: pushValue<int64_t>(popValue().doubleValue()); break; case BC_SWAP: first = popValue(); second = popValue(); pushValue(first); pushValue(second); break; case BC_POP: popValue(); break; case BC_IPRINT: writeValue(std::cout, popValue(), VT_INT); break; case BC_DPRINT: writeValue(std::cout, popValue(), VT_DOUBLE); break; case BC_SPRINT: writeValue(std::cout, popValue(), VT_STRING); break; case BC_DUMP: first = popValue(); writeValue(std::cerr, first, first.type()); break; case BC_BREAK: case BC_SLOAD0: default: throw BytecodeException("Unsupported bytecode"); } moveBci(jmp); } throw BytecodeException("STOP bytecode is not found"); }
bool CVirtualMachine::execute() { // Cache current function const SFunction ¤tFunction = m_functions.at(m_currentFunctionIndex); // Default current instruction to implicit return SInstruction instruction; instruction.id = EInstruction::Ret; // Check for out of bounds instruction index if (currentFunction.instructionSize > m_currentInstructionIndex) { // Retrieve current instruction instruction = currentFunction.instructions.at(m_currentInstructionIndex); } // Debug // printRuntimeStack(); std::cout << "Executing instruction " << toString(instruction) << std::endl; switch (instruction.id) { case EInstruction::Nop: ++m_currentInstructionIndex; break; case EInstruction::Break: // Not implemented ++m_currentInstructionIndex; break; case EInstruction::Exit: // Return false to signal end of script return false; break; case EInstruction::Movv: // Move variable to variable // Arg 0 is variable index of destination // Arg 1 is variable index of source m_runtimeStack[m_currentRuntimeStackBaseIndex + *((uint32_t *)&instruction.args[0])] = m_runtimeStack.at(m_currentRuntimeStackBaseIndex + *((uint32_t *)&instruction.args[1])); break; case EInstruction::Movi: // Move integer to variable // Arg 0 is variable index of destination // Arg 1 is source integer value m_runtimeStack[m_currentRuntimeStackBaseIndex + *((uint32_t *)&instruction.args[0])] = instruction.args[1]; break; case EInstruction::Movf: // Move float to variable // Arg 0 is variable index of destination // Arg 1 is source float value m_runtimeStack[m_currentRuntimeStackBaseIndex + *((uint32_t *)&instruction.args[0])] = *((float *)&instruction.args[1]); break; case EInstruction::Movs: // Move string to variable // Arg 0 is variable index of destination // Arg 1 is source index of string m_runtimeStack[m_currentRuntimeStackBaseIndex + *((uint32_t *)&instruction.args[0])] = m_strings.at(*((uint32_t *)&instruction.args[1])); ++m_currentInstructionIndex; break; case EInstruction::Pushi: // Push signed 32 bit integer value to // Arg 0 is integer value pushValue(CValue(instruction.args[0])); ++m_currentInstructionIndex; break; case EInstruction::Pushf: // Arg 0 is float value pushValue(CValue(*((float *)&instruction.args[0]))); ++m_currentInstructionIndex; break; case EInstruction::Pushv: // Arg 0 is variable index pushValue(m_runtimeStack.at(m_currentRuntimeStackBaseIndex + *((uint32_t *)&instruction.args[0]))); ++m_currentInstructionIndex; break; case EInstruction::Pushs: // Arg 0 is string index pushValue(m_strings.at(*((uint32_t *)&instruction.args[0]))); ++m_currentInstructionIndex; break; case EInstruction::Pop: // Remove top element from runtime stack m_runtimeStack.pop_back(); ++m_currentInstructionIndex; break; case EInstruction::Popv: { // Remove top of the stack and store it in variable // Arg 0 is variable index m_runtimeStack[m_currentRuntimeStackBaseIndex + *((uint32_t *)&instruction.args[0])] = m_runtimeStack.back(); m_runtimeStack.pop_back(); ++m_currentInstructionIndex; } break; case EInstruction::Call: { // Push current function index, next instruction index and base stack index // for // return call. m_callStack.push(SFunctionFrame(m_currentFunctionIndex, m_currentInstructionIndex + 1, m_currentRuntimeStackBaseIndex)); // Arg 0 is function index of the called function m_currentFunctionIndex = *((uint32_t *)&instruction.args[0]); // Reset instruction index m_currentInstructionIndex = 0; // Set new runtime stack base index for the called function uint32_t runtimeStackSize = static_cast<uint32_t>(m_runtimeStack.size()); m_currentRuntimeStackBaseIndex = runtimeStackSize; // Modify by function parameter size const SFunction &newCurrentFunction = m_functions.at(m_currentFunctionIndex); m_currentRuntimeStackBaseIndex -= newCurrentFunction.parameterSize; // Resize runtime stack with local stack size of called function m_runtimeStack.resize(runtimeStackSize + newCurrentFunction.stackSize - newCurrentFunction.parameterSize); break; } case EInstruction::Calle: { // Call external, arg 0 is extern function index const SExternFunction &externFunction = m_externFunctions.at(instruction.args[0]); // Check if function exists // TODO Check if arg count ok auto entry = m_externFunctionsMap.find(externFunction.name); if (entry == m_externFunctionsMap.end()) { // Function does not exist std::cout << "The extern function '" << externFunction.name << "' does not exist." << std::endl; return false; } // Call if found entry->second->call(*this); ++m_currentInstructionIndex; break; } case EInstruction::Ret: // Return from script function if (m_callStack.empty()) { // Empty stack indicates either error state or end of script return false; } // Resize runtime stack to remove local function variables. m_runtimeStack.resize(m_currentRuntimeStackBaseIndex); // Retrieve function index of previous function m_currentFunctionIndex = m_callStack.top().functionIndex; // Restore active function index m_currentInstructionIndex = m_callStack.top().instructionIndex; // Restore runtime stack base index for the active function m_currentRuntimeStackBaseIndex = m_callStack.top().runtimeStackBaseIndex; m_callStack.pop(); break; case EInstruction::Retv: { // Returns variable from script function if (m_callStack.empty()) { // Empty stack indicates either error state or end of script return false; } // Arg 0 is local variable index // Store return value at local stack position 0 uint32_t varIndex = *((uint32_t *)&instruction.args[0]); if (varIndex != 0) { m_runtimeStack[m_currentRuntimeStackBaseIndex + 1] = m_runtimeStack.at(m_currentRuntimeStackBaseIndex + varIndex); } // Resize runtime stack to remove local function variables but the one // holding the return value. m_runtimeStack.resize(m_currentRuntimeStackBaseIndex + 1); // Retrieve function index of previous function m_currentFunctionIndex = m_callStack.top().functionIndex; // Restore active function index m_currentInstructionIndex = m_callStack.top().instructionIndex; // Restore runtime stack base index for the active function m_currentRuntimeStackBaseIndex = m_callStack.top().runtimeStackBaseIndex; m_callStack.pop(); break; } case EInstruction::Reti: { // Returns variable from script function if (m_callStack.empty()) { // Empty stack indicates either error state or end of script return false; } // Arg 0 is integer constant // Store return value at local stack position 0 m_runtimeStack[m_currentRuntimeStackBaseIndex + 1] = *((int32_t *)&instruction.args[0]); // Resize runtime stack to remove local function variables but the one // holding the return value. m_runtimeStack.resize(m_currentRuntimeStackBaseIndex + 1); // Retrieve function index of previous function m_currentFunctionIndex = m_callStack.top().functionIndex; // Restore active function index m_currentInstructionIndex = m_callStack.top().instructionIndex; // Restore runtime stack base index for the active function m_currentRuntimeStackBaseIndex = m_callStack.top().runtimeStackBaseIndex; m_callStack.pop(); break; } case EInstruction::Add: { CValue x; // 2 Values needed if (!popValue(x) || m_runtimeStack.empty()) { return false; } m_runtimeStack.back() += x; ++m_currentInstructionIndex; } break; case EInstruction::Sub: { CValue x; // 2 Values needed if (!popValue(x) || m_runtimeStack.empty()) { return false; } m_runtimeStack.back() -= x; ++m_currentInstructionIndex; } break; case EInstruction::Mul: // Not implemented return false; break; case EInstruction::Div: // Not implemented return false; break; case EInstruction::Inc: // Not implemented return false; break; case EInstruction::Dec: // Not implemented return false; break; case EInstruction::And: // Not implemented return false; break; case EInstruction::Or: // Not implemented return false; break; case EInstruction::Not: // Not implemented return false; break; case EInstruction::Xor: // Not implemented return false; break; case EInstruction::Jmp: // Not implemented return false; break; case EInstruction::Je: { // Compare top 2 values from stack, x == y CValue y; CValue x; if (!popValue(y) || !popValue(x)) { // Not enough values on stack return false; } if (x == y) { // Arg 0 is target instruction index for jump uint32_t jumpIndex = *((uint32_t *)&instruction.args[0]); m_currentInstructionIndex = jumpIndex; } else { ++m_currentInstructionIndex; } } break; case EInstruction::Jne: { // Compare top 2 values from stack, x != y CValue y; CValue x; if (!popValue(y) || !popValue(x)) { // Not enough values on stack return false; } if (x != y) { // Arg 0 is target instruction index for jump uint32_t jumpIndex = *((uint32_t *)&instruction.args[0]); m_currentInstructionIndex = jumpIndex; } else { ++m_currentInstructionIndex; } } break; case EInstruction::Jle: { // Compare top 2 values from stack, x <= y CValue y; CValue x; if (!popValue(y) || !popValue(x)) { // Not enough values on stack return false; } if (x <= y) { // Arg 0 is target instruction index for jump uint32_t jumpIndex = *((uint32_t *)&instruction.args[0]); m_currentInstructionIndex = jumpIndex; } else { ++m_currentInstructionIndex; } } break; default: assert(false); return false; } return true; }