Browse Source

hacks to make thedebugger work again.

Jeff Hutchinson 4 năm trước cách đây
mục cha
commit
a449fadde2

+ 14 - 1
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<S32, StringTableEntry> variableNameMap;
+
 private:
    std::unordered_map<StringTableEntry, Var> 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;

+ 2 - 0
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);
 

+ 20 - 1
Engine/source/console/codeBlock.h

@@ -23,6 +23,24 @@
 #ifndef _CODEBLOCK_H_
 #define _CODEBLOCK_H_
 
+#include <unordered_map>
+
+struct CompilerLocalVariableToRegisterMappingTable
+{
+   // First key: function name
+   struct RemappingTable
+   {
+      std::unordered_map<StringTableEntry, S32> table;
+   };
+
+   std::unordered_map<StringTableEntry, RemappingTable> 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;

+ 4 - 4
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);

+ 41 - 0
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;

+ 3 - 0
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);
 

+ 5 - 0
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.

+ 34 - 3
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;