From a449fadde2e025507fe29bb576738f7c74a3b198 Mon Sep 17 00:00:00 2001 From: Jeff Hutchinson Date: Sat, 14 Aug 2021 01:37:01 -0400 Subject: [PATCH] hacks to make thedebugger work again. --- Engine/source/console/astNodes.cpp | 15 ++++++++- Engine/source/console/codeBlock.cpp | 2 ++ Engine/source/console/codeBlock.h | 21 +++++++++++- Engine/source/console/compiledEval.cpp | 8 ++--- Engine/source/console/compiler.cpp | 41 ++++++++++++++++++++++++ Engine/source/console/compiler.h | 3 ++ Engine/source/console/consoleInternal.h | 5 +++ Engine/source/console/telnetDebugger.cpp | 37 +++++++++++++++++++-- 8 files changed, 123 insertions(+), 9 deletions(-) diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index 65cafe70d..104671b1f 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -62,6 +62,7 @@ class FuncVars { S32 reg; TypeReq currentType; + StringTableEntry name; bool isConstant; }; @@ -76,7 +77,9 @@ public: } S32 id = counter++; - vars[var] = { id, currentType, isConstant }; + vars[var] = { id, currentType, var, isConstant }; + variableNameMap[id] = var; + return id; } @@ -97,6 +100,8 @@ public: inline S32 count() { return counter; } + std::unordered_map variableNameMap; + private: std::unordered_map vars; S32 counter = 0; @@ -1604,6 +1609,14 @@ U32 FunctionDeclStmtNode::compileStmt(CodeStream& codeStream, U32 ip) setCurrentStringTable(&getGlobalStringTable()); setCurrentFloatTable(&getGlobalFloatTable()); + + // map local variables to registers for this function. + CompilerLocalVariableToRegisterMappingTable* tbl = &getFunctionVariableMappingTable(); + for (const auto& pair : gFuncVars->variableNameMap) + { + tbl->add(fnName, pair.second, pair.first); + } + gFuncVars = NULL; return ip; diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp index e7c3d4adb..b58156c4a 100644 --- a/Engine/source/console/codeBlock.cpp +++ b/Engine/source/console/codeBlock.cpp @@ -640,6 +640,8 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr globalFloats = getGlobalFloatTable().build(); functionFloats = getFunctionFloatTable().build(); + variableRegisterTable = getFunctionVariableMappingTable().copy(); + codeStream.emit(OP_RETURN_VOID); codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs); diff --git a/Engine/source/console/codeBlock.h b/Engine/source/console/codeBlock.h index 1daa26bac..be6e48a46 100644 --- a/Engine/source/console/codeBlock.h +++ b/Engine/source/console/codeBlock.h @@ -23,6 +23,24 @@ #ifndef _CODEBLOCK_H_ #define _CODEBLOCK_H_ +#include + +struct CompilerLocalVariableToRegisterMappingTable +{ + // First key: function name + struct RemappingTable + { + std::unordered_map table; + }; + + std::unordered_map localVarToRegister; + + void add(StringTableEntry functionName, StringTableEntry varName, S32 reg); + S32 lookup(StringTableEntry functionName, StringTableEntry varName); + CompilerLocalVariableToRegisterMappingTable copy(); + void reset(); +}; + #include "console/compiler.h" #include "console/consoleParser.h" @@ -34,7 +52,6 @@ class ConsoleValue; /// This class represents a block of code, usually mapped directly to a file. class CodeBlock { - friend class CodeInterpreter; private: static CodeBlock* smCodeBlockList; static CodeBlock* smCurrentCodeBlock; @@ -77,6 +94,8 @@ public: U32 codeSize; U32 *code; + CompilerLocalVariableToRegisterMappingTable variableRegisterTable; + U32 refCount; U32 lineBreakPairCount; U32 *lineBreakPairs; diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index 10d201874..b80530162 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -1773,8 +1773,8 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa //if this is called from inside a function, append the ip and codeptr if (!gEvalState.stack.empty()) { - gEvalState.stack.last()->code = this; - gEvalState.stack.last()->ip = ip - 1; + gEvalState.getCurrentFrame().code = this; + gEvalState.getCurrentFrame().ip = ip - 1; } ip += 5; @@ -2070,8 +2070,8 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa { //append the ip and codeptr before managing the breakpoint! AssertFatal(!gEvalState.stack.empty(), "Empty eval stack on break!"); - gEvalState.stack.last()->code = this; - gEvalState.stack.last()->ip = ip - 1; + gEvalState.getCurrentFrame().code = this; + gEvalState.getCurrentFrame().ip = ip - 1; U32 breakLine; findBreakLine(ip - 1, breakLine, instruction); diff --git a/Engine/source/console/compiler.cpp b/Engine/source/console/compiler.cpp index 8b1e15a8f..969770d54 100644 --- a/Engine/source/console/compiler.cpp +++ b/Engine/source/console/compiler.cpp @@ -60,6 +60,7 @@ namespace Compiler CompilerFloatTable *gCurrentFloatTable, gGlobalFloatTable, gFunctionFloatTable; DataChunker gConsoleAllocator; CompilerIdentTable gIdentTable; + CompilerLocalVariableToRegisterMappingTable gFunctionVariableMappingTable; //------------------------------------------------------------ @@ -92,6 +93,8 @@ namespace Compiler CompilerStringTable &getGlobalStringTable() { return gGlobalStringTable; } CompilerStringTable &getFunctionStringTable() { return gFunctionStringTable; } + CompilerLocalVariableToRegisterMappingTable& getFunctionVariableMappingTable() { return gFunctionVariableMappingTable; } + void setCurrentStringTable(CompilerStringTable* cst) { gCurrentStringTable = cst; } CompilerFloatTable *getCurrentFloatTable() { return gCurrentFloatTable; } @@ -117,6 +120,7 @@ namespace Compiler getFunctionFloatTable().reset(); getFunctionStringTable().reset(); getIdentTable().reset(); + getFunctionVariableMappingTable().reset(); } void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); } @@ -208,6 +212,43 @@ void CompilerStringTable::write(Stream &st) //------------------------------------------------------------ +void CompilerLocalVariableToRegisterMappingTable::add(StringTableEntry functionName, StringTableEntry varName, S32 reg) +{ + localVarToRegister[functionName].table[varName] = reg; +} + +S32 CompilerLocalVariableToRegisterMappingTable::lookup(StringTableEntry functionName, StringTableEntry varName) +{ + auto functionPosition = localVarToRegister.find(functionName); + if (functionPosition != localVarToRegister.end()) + { + const auto& table = localVarToRegister[functionName].table; + auto varPosition = table.find(varName); + if (varPosition != table.end()) + { + return varPosition->second; + } + } + + Con::errorf("Unable to find local variable %s in function name %s", varName, functionName); + return -1; +} + +CompilerLocalVariableToRegisterMappingTable CompilerLocalVariableToRegisterMappingTable::copy() +{ + // Trivilly copyable as its all plain old data and using STL containers... (We want a deep copy though!) + CompilerLocalVariableToRegisterMappingTable table; + table.localVarToRegister = localVarToRegister; + return std::move(table); +} + +void CompilerLocalVariableToRegisterMappingTable::reset() +{ + localVarToRegister.clear(); +} + +//------------------------------------------------------------ + U32 CompilerFloatTable::add(F64 value) { Entry **walk; diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 4e6676d4c..117576a61 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -44,6 +44,8 @@ class DataChunker; #include "core/util/tVector.h" #endif +//------------------------------------------------------------ + namespace Compiler { /// The opcodes for the TorqueScript VM. @@ -252,6 +254,7 @@ namespace Compiler CompilerStringTable *getCurrentStringTable(); CompilerStringTable &getGlobalStringTable(); CompilerStringTable &getFunctionStringTable(); + CompilerLocalVariableToRegisterMappingTable& getFunctionVariableMappingTable(); void setCurrentStringTable(CompilerStringTable* cst); diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h index f947057e5..6b7cc2294 100644 --- a/Engine/source/console/consoleInternal.h +++ b/Engine/source/console/consoleInternal.h @@ -644,6 +644,11 @@ public: return *(stack[mStackDepth - 1]); } + Dictionary& getFrameAt(S32 depth) + { + return *(stack[depth]); + } + /// @} /// Run integrity checks for debugging. diff --git a/Engine/source/console/telnetDebugger.cpp b/Engine/source/console/telnetDebugger.cpp index 719ed7d32..306611b0a 100644 --- a/Engine/source/console/telnetDebugger.cpp +++ b/Engine/source/console/telnetDebugger.cpp @@ -864,6 +864,37 @@ void TelnetDebugger::evaluateExpression(const char *tag, S32 frame, const char * if ( frame < 0 ) frame = 0; + // Local variables use their own memory management and can't be queried by just executing + // TorqueScript, we have to go digging into the interpreter. + S32 evalBufferLen = dStrlen(evalBuffer); + bool isEvaluatingLocalVariable = evalBufferLen > 0 && evalBuffer[0] == '%'; + if (isEvaluatingLocalVariable) + { + const char* format = "EVALOUT %s %s\r\n"; + + Dictionary &stackFrame = gEvalState.getFrameAt(frame); + StringTableEntry functionName = stackFrame.scopeName; + S32 registerId = stackFrame.code->variableRegisterTable.lookup(functionName, StringTable->insert(evalBuffer)); + + if (registerId == -1) + { + // ERROR, can't read the variable! + send("EVALOUT \"\" \"\""); + return; + } + + const char* varResult = gEvalState.getLocalStringVariable(registerId); + + S32 len = dStrlen(format) + dStrlen(tag) + dStrlen(varResult); + char* buffer = new char[len]; + dSprintf(buffer, len, format, tag, varResult[0] ? varResult : "\"\""); + + send(buffer); + delete[] buffer; + + return; + } + // Build a buffer just big enough for this eval. const char* format = "return %s;"; dsize_t len = dStrlen( format ) + dStrlen( evalBuffer ); @@ -872,14 +903,14 @@ void TelnetDebugger::evaluateExpression(const char *tag, S32 frame, const char * // Execute the eval. CodeBlock *newCodeBlock = new CodeBlock(); - const char* result = newCodeBlock->compileExec( NULL, buffer, false, frame ); + ConsoleValue result = newCodeBlock->compileExec( NULL, buffer, false, frame ); delete [] buffer; // Create a new buffer that fits the result. format = "EVALOUT %s %s\r\n"; - len = dStrlen( format ) + dStrlen( tag ) + dStrlen( result ); + len = dStrlen( format ) + dStrlen( tag ) + dStrlen( result.getString() ); buffer = new char[ len ]; - dSprintf( buffer, len, format, tag, result[0] ? result : "\"\"" ); + dSprintf( buffer, len, format, tag, result.getString()[0] ? result.getString() : "\"\"" ); send( buffer ); delete [] buffer;