Bläddra i källkod

Merge pull request #587 from JeffProgrammer/local-evals

Allow local variables to be used in eval.
Brian Roberts 4 år sedan
förälder
incheckning
f83b528f3c

+ 3 - 52
Engine/source/console/astNodes.cpp

@@ -56,57 +56,7 @@ namespace Compiler
 
 using namespace Compiler;
 
-class FuncVars
-{
-   struct Var
-   {
-      S32 reg;
-      TypeReq currentType;
-      StringTableEntry name;
-      bool isConstant;
-   };
-
-public:
-   S32 assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant = false)
-   {
-      std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
-      if (found != vars.end())
-      {
-         AssertISV(!found->second.isConstant, avar("Reassigning variable %s when it is a constant. File: %s Line : %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber));
-         return found->second.reg;
-      }
-
-      S32 id = counter++;
-      vars[var] = { id, currentType, var, isConstant };
-      variableNameMap[id] = var;
-
-      return id;
-   }
-
-   S32 lookup(StringTableEntry var, S32 lineNumber)
-   {
-      std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
-      AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber));
-      return found->second.reg;
-   }
-
-   TypeReq lookupType(StringTableEntry var, S32 lineNumber)
-   {
-      std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
-
-      AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber));
-      return found->second.currentType;
-   }
-
-   inline S32 count() { return counter; }
-
-   std::unordered_map<S32, StringTableEntry> variableNameMap;
-
-private:
-   std::unordered_map<StringTableEntry, Var> vars;
-   S32 counter = 0;
-};
-
+FuncVars gEvalFuncVars;
 FuncVars* gFuncVars = NULL;
 
 inline FuncVars* getFuncVars(S32 lineNumber)
@@ -1602,7 +1552,8 @@ U32 FunctionDeclStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
       tbl->add(fnName, nameSpace, varName);
    }
 
-   gFuncVars = NULL;
+   // In eval mode, global func vars are allowed.
+   gFuncVars = gIsEvalCompile ? &gEvalFuncVars : NULL;
 
    return ip;
 }

+ 14 - 1
Engine/source/console/codeBlock.cpp

@@ -37,6 +37,9 @@ CodeBlock *    CodeBlock::smCodeBlockList = NULL;
 CodeBlock *    CodeBlock::smCurrentCodeBlock = NULL;
 ConsoleParser *CodeBlock::smCurrentParser = NULL;
 
+extern FuncVars gEvalFuncVars;
+extern FuncVars* gFuncVars;
+
 //-------------------------------------------------------------------------
 
 CodeBlock::CodeBlock()
@@ -491,6 +494,8 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con
    chompUTF8BOM(inScript, &script);
 
    gSyntaxError = false;
+   gIsEvalCompile = false;
+   gFuncVars = NULL;
 
    consoleAllocReset();
 
@@ -629,6 +634,11 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr
       addToCodeList();
 
    gStatementList = NULL;
+   
+   // we are an eval compile if we don't have a file name associated (no exec)
+   gIsEvalCompile = fileName == NULL;
+   // In eval mode, global func vars are allowed.
+   gFuncVars = gIsEvalCompile ? &gEvalFuncVars : NULL;
 
    // Set up the parser.
    smCurrentParser = getParserForFile(fileName);
@@ -667,6 +677,8 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr
 
    codeStream.emit(OP_RETURN_VOID);
    codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
+   
+   S32 localRegisterCount = gIsEvalCompile ? gEvalFuncVars.count() : 0;
 
    consoleAllocReset();
 
@@ -683,7 +695,8 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr
    if (lastIp + 1 != codeSize)
       Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp);
 
-   return std::move(exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame));
+   // repurpose argc as local register counter for global state
+   return std::move(exec(0, fileName, NULL, localRegisterCount, 0, noCalls, NULL, setFrame));
 }
 
 //-------------------------------------------------------------------------

+ 2 - 1
Engine/source/console/compiledEval.cpp

@@ -693,7 +693,8 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
       // Do we want this code to execute using a new stack frame?
       if (setFrame < 0)
       {
-         gEvalState.pushFrame(NULL, NULL, 0);
+         // argc is the local count for eval
+         gEvalState.pushFrame(NULL, NULL, argc);
          gCallStack.pushFrame(0);
          popFrame = true;
       }

+ 42 - 0
Engine/source/console/compiler.cpp

@@ -34,6 +34,8 @@
 
 #include "console/simBase.h"
 
+extern FuncVars gEvalFuncVars;
+
 namespace Compiler
 {
 
@@ -86,6 +88,7 @@ namespace Compiler
    //------------------------------------------------------------
 
    bool gSyntaxError = false;
+   bool gIsEvalCompile = false;
 
    //------------------------------------------------------------
 
@@ -121,6 +124,7 @@ namespace Compiler
       getFunctionStringTable().reset();
       getIdentTable().reset();
       getFunctionVariableMappingTable().reset();
+      gEvalFuncVars.clear();
    }
 
    void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); }
@@ -132,6 +136,44 @@ namespace Compiler
 
 using namespace Compiler;
 
+S32 FuncVars::assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant)
+{
+   std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
+   if (found != vars.end())
+   {
+      AssertISV(!found->second.isConstant, avar("Reassigning variable %s when it is a constant. File: %s Line : %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber));
+      return found->second.reg;
+   }
+
+   S32 id = counter++;
+   vars[var] = { id, currentType, var, isConstant };
+   variableNameMap[id] = var;
+
+   return id;
+}
+
+S32 FuncVars::lookup(StringTableEntry var, S32 lineNumber)
+{
+   std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
+   AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber));
+   return found->second.reg;
+}
+
+TypeReq FuncVars::lookupType(StringTableEntry var, S32 lineNumber)
+{
+   std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
+
+   AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber));
+   return found->second.currentType;
+}
+
+void FuncVars::clear()
+{
+   vars.clear();
+   variableNameMap.clear();
+   counter = 0;
+}
+
 //-------------------------------------------------------------------------
 
 

+ 29 - 0
Engine/source/console/compiler.h

@@ -276,6 +276,35 @@ namespace Compiler
    void consoleAllocReset();
 
    extern bool gSyntaxError;
+   extern bool gIsEvalCompile;
+};
+
+class FuncVars
+{
+   struct Var
+   {
+      S32 reg;
+      TypeReq currentType;
+      StringTableEntry name;
+      bool isConstant;
+   };
+
+public:
+   S32 assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant = false);
+
+   S32 lookup(StringTableEntry var, S32 lineNumber);
+
+   TypeReq lookupType(StringTableEntry var, S32 lineNumber);
+   
+   inline S32 count() { return counter; }
+
+   std::unordered_map<S32, StringTableEntry> variableNameMap;
+
+   void clear();
+   
+private:
+   std::unordered_map<StringTableEntry, Var> vars;
+   S32 counter = 0;
 };
 
 /// Utility class to emit and patch bytecode