Przeglądaj źródła

fix foreach/foreach$ loops.

Jeff Hutchinson 4 lat temu
rodzic
commit
4e678292e1

+ 9 - 2
Engine/source/console/astNodes.cpp

@@ -413,11 +413,18 @@ U32 IterStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
 
 
    codeStream.pushFixScope(true);
    codeStream.pushFixScope(true);
 
 
+   bool isGlobal = varName[0] == '$';
+   TypeReq varType = isStringIter ? TypeReqString : TypeReqUInt;
+
    const U32 startIp = ip;
    const U32 startIp = ip;
-   containerExpr->compile(codeStream, startIp, TypeReqString);
+   containerExpr->compile(codeStream, startIp, TypeReqString); // todo: figure out better way to codegen this so we don't rely on STR
 
 
    codeStream.emit(isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN);
    codeStream.emit(isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN);
-   codeStream.emitSTE(varName);
+   codeStream.emit(isGlobal);
+   if (isGlobal)
+      codeStream.emitSTE(varName);
+   else
+      codeStream.emit(gFuncVars->assign(varName, varType));
    const U32 finalFix = codeStream.emit(0);
    const U32 finalFix = codeStream.emit(0);
    const U32 continueIp = codeStream.emit(OP_ITER);
    const U32 continueIp = codeStream.emit(OP_ITER);
    codeStream.emitFix(CodeStream::FIXTYPE_BREAK);
    codeStream.emitFix(CodeStream::FIXTYPE_BREAK);

+ 37 - 9
Engine/source/console/codeBlock.cpp

@@ -643,7 +643,8 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr
    codeStream.emit(OP_RETURN);
    codeStream.emit(OP_RETURN);
    codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
    codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
 
 
-   //dumpInstructions(0, false);
+   if (Con::getBoolVariable("dump"))
+   dumpInstructions(0, false);
 
 
    consoleAllocReset();
    consoleAllocReset();
 
 
@@ -1392,23 +1393,50 @@ void CodeBlock::dumpInstructions(U32 startIp, bool upToReturn)
 
 
       case OP_ITER_BEGIN:
       case OP_ITER_BEGIN:
       {
       {
-         StringTableEntry varName = CodeToSTE(code, ip);
-         U32 failIp = code[ip + 2];
+         bool isGlobal = code[ip];
+         if (isGlobal)
+         {
+            StringTableEntry varName = CodeToSTE(code, ip + 1);
+            U32 failIp = code[ip + 3];
+
+            Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i isGlobal=%s", ip - 1, varName, failIp, "true");
+
+            ip += 4;
+         }
+         else
+         {
+            S32 reg = code[ip + 1];
+            U32 failIp = code[ip + 2];
 
 
-         Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
+            Con::printf("%i: OP_ITER_BEGIN varRegister=%d failIp=%i isGlobal=%s", ip - 1, reg, failIp, "false");
 
 
-         ip += 3;
+            ip += 3;
+         }
          break;
          break;
       }
       }
 
 
       case OP_ITER_BEGIN_STR:
       case OP_ITER_BEGIN_STR:
       {
       {
-         StringTableEntry varName = CodeToSTE(code, ip);
-         U32 failIp = code[ip + 2];
+         bool isGlobal = code[ip];
+         if (isGlobal)
+         {
+            StringTableEntry varName = CodeToSTE(code, ip + 1);
+            U32 failIp = code[ip + 3];
 
 
-         Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
+            Con::printf("%i: OP_ITER_BEGIN_STR varName=%s failIp=%i isGlobal=%s", ip - 1, varName, failIp, "true");
+
+            ip += 4;
+         }
+         else
+         {
+            S32 reg = code[ip + 1];
+            U32 failIp = code[ip + 2];
+
+            Con::printf("%i: OP_ITER_BEGIN_STR varRegister=%d failIp=%i isGlobal=%s", ip - 1, reg, failIp, "false");
+
+            ip += 3;
+         }
 
 
-         ip += 3;
          break;
          break;
       }
       }
 
 

+ 46 - 9
Engine/source/console/compiledEval.cpp

@@ -66,8 +66,18 @@ struct IterStackRecord
    /// If true, this is a foreach$ loop; if not, it's a foreach loop.
    /// If true, this is a foreach$ loop; if not, it's a foreach loop.
    bool mIsStringIter;
    bool mIsStringIter;
 
 
-   /// The iterator variable.
-   Dictionary::Entry* mVariable;
+   /// True if the variable referenced is a global
+   bool mIsGlobalVariable;
+
+   union
+   {
+
+      /// The iterator variable if we are a global variable
+      Dictionary::Entry* mVariable;
+
+      /// The register variable if we are a local variable
+      S32 mRegister;
+   } mVar;
 
 
    /// Information for an object iterator loop.
    /// Information for an object iterator loop.
    struct ObjectPos
    struct ObjectPos
@@ -1745,6 +1755,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
          {
          {
             if (nsEntry->mFunctionOffset)
             if (nsEntry->mFunctionOffset)
             {
             {
+               // TODO: not make this strings only for returns.
                ConsoleValue returnFromFn = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage);
                ConsoleValue returnFromFn = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage);
                STR.setStringValue(returnFromFn.getString());
                STR.setStringValue(returnFromFn.getString());
             }
             }
@@ -1954,12 +1965,22 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
 
 
       case OP_ITER_BEGIN:
       case OP_ITER_BEGIN:
       {
       {
-         StringTableEntry varName = CodeToSTE(code, ip);
-         U32 failIp = code[ip + 2];
+         bool isGlobal = code[ip];
+
+         U32 failIp = code[ip + isGlobal ? 3 : 2];
 
 
          IterStackRecord& iter = iterStack[_ITER];
          IterStackRecord& iter = iterStack[_ITER];
+         iter.mIsGlobalVariable = isGlobal;
 
 
-         iter.mVariable = gEvalState.getCurrentFrame().add(varName);
+         if (isGlobal)
+         {
+            StringTableEntry varName = CodeToSTE(code, ip + 1);
+            iter.mVar.mVariable = gEvalState.globalVars.add(varName);
+         }
+         else
+         {
+            iter.mVar.mRegister = code[ip + 1];
+         }
 
 
          if (iter.mIsStringIter)
          if (iter.mIsStringIter)
          {
          {
@@ -1990,7 +2011,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
 
 
          STR.push();
          STR.push();
 
 
-         ip += 3;
+         ip += isGlobal ? 4 : 3;
          break;
          break;
       }
       }
 
 
@@ -2026,11 +2047,21 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
             {
             {
                char savedChar = str[endIndex];
                char savedChar = str[endIndex];
                const_cast<char*>(str)[endIndex] = '\0'; // We are on the string stack so this is okay.
                const_cast<char*>(str)[endIndex] = '\0'; // We are on the string stack so this is okay.
-               iter.mVariable->setStringValue(&str[startIndex]);
+
+               if (iter.mIsGlobalVariable)
+                  iter.mVar.mVariable->setStringValue(&str[startIndex]);
+               else
+                  gEvalState.setLocalStringVariable(iter.mVar.mRegister, &str[startIndex], endIndex - startIndex);
+
                const_cast<char*>(str)[endIndex] = savedChar;
                const_cast<char*>(str)[endIndex] = savedChar;
             }
             }
             else
             else
-               iter.mVariable->setStringValue("");
+            {
+               if (iter.mIsGlobalVariable)
+                  iter.mVar.mVariable->setStringValue("");
+               else
+                  gEvalState.setLocalStringVariable(iter.mVar.mRegister, "", 0);
+            }
 
 
             // Skip separator.
             // Skip separator.
             if (str[endIndex] != '\0')
             if (str[endIndex] != '\0')
@@ -2049,7 +2080,13 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
                continue;
                continue;
             }
             }
 
 
-            iter.mVariable->setIntValue(set->at(index)->getId());
+            SimObjectId id = set->at(index)->getId();
+
+            if (iter.mIsGlobalVariable)
+               iter.mVar.mVariable->setIntValue(id);
+            else
+               gEvalState.setLocalIntVariable(iter.mVar.mRegister, id);
+
             iter.mData.mObj.mIndex = index + 1;
             iter.mData.mObj.mIndex = index + 1;
          }
          }
 
 

+ 84 - 4
Engine/source/console/test/ScriptTest.cpp

@@ -241,9 +241,11 @@ TEST(Script, Basic_Loop_Statements)
    ASSERT_STREQ(forValue.getString(), "aaa");
    ASSERT_STREQ(forValue.getString(), "aaa");
 
 
    ConsoleValue forIfValue = RunScript(R"(
    ConsoleValue forIfValue = RunScript(R"(
-         function t() {
+         function t()
+         {
             %str = "";
             %str = "";
-            for (%i = 0; %i < 5; %i++) {
+            for (%i = 0; %i < 5; %i++)
+            {
 
 
                %loopValue = %i;
                %loopValue = %i;
 
 
@@ -261,6 +263,82 @@ TEST(Script, Basic_Loop_Statements)
    ASSERT_STREQ(forIfValue.getString(), "0, 1, 2, 3, 4");
    ASSERT_STREQ(forIfValue.getString(), "0, 1, 2, 3, 4");
 }
 }
 
 
+TEST(Script, ForEachLoop)
+{
+   ConsoleValue forEach1 = RunScript(R"(
+         $theSimSet = new SimSet();
+         $theSimSet.add(new SimObject());
+         $theSimSet.add(new SimObject());
+
+         $counter = 0;
+         foreach ($obj in $theSimSet)
+         {
+            $counter++;
+         }
+
+         $theSimSet.delete();
+
+         return $counter;
+   )");
+
+   ASSERT_EQ(forEach1.getInt(), 2);
+
+   ConsoleValue forEach2 = RunScript(R"(
+         $counter = 0;
+         foreach$ ($word in "a b c d")
+         {
+            $counter++;
+         }
+
+         return $counter;
+   )");
+
+   ASSERT_EQ(forEach2.getInt(), 4);
+
+   ConsoleValue forEach3 = RunScript(R"(
+         function SimObject::addOne(%this)
+         {
+            return 1;
+         }
+
+         function test()
+         {
+            %set = new SimSet();
+            %set.add(new SimObject());
+            %set.add(new SimObject());
+
+            %count = 0;
+            foreach (%obj in %set)
+               %count += %obj.addOne();
+
+            %set.delete();
+
+            return %count;
+         }
+
+         return test();
+   )");
+
+   ASSERT_EQ(forEach3.getInt(), 2);
+
+   ConsoleValue forEach4 = RunScript(R"(
+         function test()
+         {
+            %string = "a b c d e";
+
+            %count = 0;
+            foreach$ (%word in %string)
+               %count++;
+
+            return %count;
+         }
+
+         return test();
+   )");
+
+   ASSERT_EQ(forEach4.getInt(), 5);
+}
+
 TEST(Script, TorqueScript_Array_Testing)
 TEST(Script, TorqueScript_Array_Testing)
 {
 {
    ConsoleValue value = RunScript(R"(
    ConsoleValue value = RunScript(R"(
@@ -281,7 +359,8 @@ TEST(Script, TorqueScript_Array_Testing)
 TEST(Script, Basic_SimObject)
 TEST(Script, Basic_SimObject)
 {
 {
    ConsoleValue object = RunScript(R"(
    ConsoleValue object = RunScript(R"(
-         return new SimObject(FudgeCollector) {
+         return new SimObject(FudgeCollector)
+         {
             fudge = "Chocolate"; 
             fudge = "Chocolate"; 
          };
          };
    )");
    )");
@@ -329,7 +408,8 @@ TEST(Script, Basic_Package)
 {
 {
    ConsoleValue value = RunScript(R"(
    ConsoleValue value = RunScript(R"(
          function a() { return 3; }
          function a() { return 3; }
-         package overrides {
+         package overrides
+         {
             function a() { return 5; }
             function a() { return 5; }
          };
          };
          return a();
          return a();