diff --git a/Engine/source/console/astAlloc.cpp b/Engine/source/console/astAlloc.cpp index 58d5a0d7f..50d02bcaa 100644 --- a/Engine/source/console/astAlloc.cpp +++ b/Engine/source/console/astAlloc.cpp @@ -254,7 +254,7 @@ StrConstNode* StrConstNode::alloc(S32 lineNumber, char* str, bool tag, bool doc) ret->str = (char*)consoleAlloc(len + 1); ret->tag = tag; ret->doc = doc; - dStrcpy(ret->str, str, len); + dStrcpy(ret->str, str, len + 1); ret->str[len] = '\0'; return ret; diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index bffe06980..0375ab14e 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -512,6 +512,12 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa curStringTable = globalStrings; curStringTableLen = globalStringsMaxLen; + // If requested stack frame isn't available, request a new one + // (this prevents assert failures when creating local + // variables without a stack frame) + if (gEvalState.getStackDepth() <= setFrame) + setFrame = -1; + // Do we want this code to execute using a new stack frame? if (setFrame < 0) { @@ -519,13 +525,13 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa gCallStack.pushFrame(0); popFrame = true; } - else if (!gEvalState.stack.empty()) + else { // We want to copy a reference to an existing stack frame // on to the top of the stack. Any change that occurs to // the locals during this new frame will also occur in the // original frame. - S32 stackIndex = gEvalState.stack.size() - setFrame - 1; + S32 stackIndex = gEvalState.getTopOfStack() - setFrame - 1; gEvalState.pushFrameRef(stackIndex); popFrame = true; } @@ -1062,7 +1068,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa // We're falling thru here on purpose. case OP_RETURN: - + { if (iterDepth > 0) { // Clear iterator state. @@ -1074,13 +1080,13 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa const char* retVal = STR.getStringValue(); STR.rewind(); - - returnValue.setString(retVal, STR.mLen); STR.setStringValue(retVal); // Not nice but works. } - goto execFinished; + returnValue.setString(STR.getStringValue(), STR.mLen); + goto execFinished; + } case OP_RETURN_FLT: if (iterDepth > 0) @@ -1739,8 +1745,8 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa { if (nsEntry->mFunctionOffset) { - const char* ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage).getString(); - STR.setStringValue(ret); + ConsoleValue returnFromFn = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage); + STR.setStringValue(returnFromFn.getString()); } else // no body STR.setStringValue(""); diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index dfdb90010..763adc564 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -203,6 +203,7 @@ public: break; } + ref.data = NULL; ref.setEmptyString(); } @@ -283,7 +284,7 @@ public: TORQUE_FORCEINLINE void setString(const char* val) { - setString(val, dStrlen(val) + 1); + setString(val, dStrlen(val)); } TORQUE_FORCEINLINE void setString(const char* val, S32 len) @@ -298,9 +299,9 @@ public: type = ConsoleValueType::cvString; - s = (char*)dMalloc(len + 1); - s[len] = 0x0; - dStrcpy(s, val, len); + s = (char*)dMalloc(static_cast(len) + 1); + s[len] = '\0'; + dStrcpy(s, val, static_cast(len) + 1); } TORQUE_FORCEINLINE void setBool(const bool val) diff --git a/Engine/source/console/consoleInternal.cpp b/Engine/source/console/consoleInternal.cpp index 0444369f2..336de99fa 100644 --- a/Engine/source/console/consoleInternal.cpp +++ b/Engine/source/console/consoleInternal.cpp @@ -637,6 +637,8 @@ void ExprEvalState::pushFrame(StringTableEntry frameName, Namespace *ns, S32 reg localStack.push_back(ConsoleValueFrame(consoleValArray, false)); currentRegisterArray = &localStack.last(); + AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size())); + #ifdef DEBUG_SPEW validate(); #endif @@ -664,6 +666,8 @@ void ExprEvalState::popFrame() currentRegisterArray = localStack.size() ? &localStack.last() : NULL; + AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size())); + #ifdef DEBUG_SPEW validate(); #endif @@ -671,7 +675,7 @@ void ExprEvalState::popFrame() void ExprEvalState::pushFrameRef(S32 stackIndex) { - AssertFatal(stackIndex >= 0 && stackIndex < stack.size(), "You must be asking for a valid frame!"); + AssertFatal(stackIndex >= 0 && stackIndex < mStackDepth, "You must be asking for a valid frame!"); #ifdef DEBUG_SPEW validate(); @@ -695,6 +699,12 @@ void ExprEvalState::pushFrameRef(S32 stackIndex) mStackDepth++; currentVariable = NULL; + ConsoleValue* values = localStack[stackIndex].values; + localStack.push_back(ConsoleValueFrame(values, true)); + currentRegisterArray = &localStack.last(); + + AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size())); + #ifdef DEBUG_SPEW validate(); #endif diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h index 085a7a0e5..e1d2cd462 100644 --- a/Engine/source/console/consoleInternal.h +++ b/Engine/source/console/consoleInternal.h @@ -494,6 +494,8 @@ public: /// an interior pointer that will become invalid when the object changes address. Vector< Dictionary* > stack; + S32 getTopOfStack() { return (S32)mStackDepth; } + Vector< ConsoleValueFrame > localStack; ConsoleValueFrame* currentRegisterArray; // contains array at to top of localStack diff --git a/Engine/source/console/engineAPI.h b/Engine/source/console/engineAPI.h index 5cfa7cc09..a8540968a 100644 --- a/Engine/source/console/engineAPI.h +++ b/Engine/source/console/engineAPI.h @@ -151,7 +151,7 @@ template< typename T > inline void EngineMarshallData( const T& arg, S32& argc, ConsoleValue *argv ) { const char* str = castConsoleTypeToString(arg);; - argv[ argc++ ].setString(str, dStrlen(str)); + argv[ argc++ ].setString(str); } inline void EngineMarshallData( bool arg, S32& argc, ConsoleValue *argv ) { diff --git a/Engine/source/console/test/ScriptTest.cpp b/Engine/source/console/test/ScriptTest.cpp index 1b937c755..3bd6a1bb4 100644 --- a/Engine/source/console/test/ScriptTest.cpp +++ b/Engine/source/console/test/ScriptTest.cpp @@ -30,139 +30,99 @@ #include "math/mMath.h" #include "console/stringStack.h" -template -inline T Convert(ConsoleValue&); - -template<> -inline U32 Convert(ConsoleValue &val) +inline ConsoleValue RunScript(const char* str) { - return val.getInt(); -} - -template<> -inline S32 Convert(ConsoleValue &val) -{ - return val.getInt(); -} - -template<> -inline bool Convert(ConsoleValue &val) -{ - return val.getBool(); -} - -template<> -inline F32 Convert(ConsoleValue &val) -{ - return val.getFloat(); -} - -template<> -inline const char* Convert(ConsoleValue &val) -{ - return val.getString(); -} - -template<> -inline SimObject* Convert(ConsoleValue &val) -{ - return Sim::findObject(val); -} - -template -inline T RunScript(const char* str) -{ - return Convert(std::move(Con::evaluate(str, false, NULL))); + return std::move(Con::evaluate(str, false, NULL)); } TEST(Script, Basic_Arithmetic) { - S32 add = RunScript(R"( + ConsoleValue add = RunScript(R"( return 1.0 + 1; )"); - EXPECT_EQ(add, 2); + ASSERT_EQ(add.getInt(), 2); - S32 sub = RunScript(R"( + ConsoleValue sub = RunScript(R"( return 10 - 1.0; )"); - EXPECT_EQ(sub, 9); + ASSERT_EQ(sub.getInt(), 9); - S32 mult = RunScript(R"( + ConsoleValue mult = RunScript(R"( return 10 * 2.5; )"); - EXPECT_EQ(mult, 25); + ASSERT_EQ(mult.getInt(), 25); - S32 div = RunScript(R"( + ConsoleValue div = RunScript(R"( return 10.0 / 2; )"); - EXPECT_EQ(div, 5); + ASSERT_EQ(div.getInt(), 5); } TEST(Script, Complex_Arithmetic) { - S32 result = RunScript(R"( + ConsoleValue result = RunScript(R"( return 1 * 2 - (0.5 * 2); )"); - EXPECT_EQ(result, 1); + ASSERT_EQ(result.getInt(), 1); - S32 result2 = RunScript(R"( + ConsoleValue result2 = RunScript(R"( return 1 * 2 * 3 % 2; )"); - EXPECT_EQ(result2, 0); + ASSERT_EQ(result2.getInt(), 0); } TEST(Script, Basic_Concatination) { - const char* result1 = RunScript(R"( + ConsoleValue result1 = RunScript(R"( return "a" @ "b"; )"); - EXPECT_STREQ(result1, "ab"); + ASSERT_STREQ(result1.getString(), "ab"); - const char* result2 = RunScript(R"( + ConsoleValue result2 = RunScript(R"( return "a" SPC "b"; )"); - EXPECT_STREQ(result2, "a b"); + ASSERT_STREQ(result2.getString(), "a b"); - const char* result3 = RunScript(R"( + ConsoleValue result3 = RunScript(R"( return "a" TAB "b"; )"); - EXPECT_STREQ(result3, "a\tb"); + ASSERT_STREQ(result3.getString(), "a\tb"); - const char* result4 = RunScript(R"( + ConsoleValue result4 = RunScript(R"( return "a" NL "b"; )"); - EXPECT_STREQ(result4, "a\nb"); + ASSERT_STREQ(result4.getString(), "a\nb"); - const char* complex = RunScript(R"( + ConsoleValue complex = RunScript(R"( return "a" @ "b" @ "c" @ "d"; )"); - EXPECT_STREQ(complex, "abcd"); + ASSERT_STREQ(complex.getString(), "abcd"); } TEST(Script, Basic_Global_Variable_Tests) { - S32 value = RunScript(R"( + ConsoleValue value = RunScript(R"( $a = 1; return $a; )"); - EXPECT_EQ(value, 1); + ASSERT_EQ(value.getInt(), 1); } TEST(Script, Variable_Chaining_And_Usage) { - S32 value = RunScript(R"( + ConsoleValue value = RunScript(R"( function t() { %a = %b = 2; @@ -171,9 +131,9 @@ TEST(Script, Variable_Chaining_And_Usage) return t(); )"); - EXPECT_EQ(value, 2); + ASSERT_EQ(value.getInt(), 2); - S32 valueGlobal = RunScript(R"( + ConsoleValue valueGlobal = RunScript(R"( function t() { $a = %b = 2; @@ -182,9 +142,9 @@ TEST(Script, Variable_Chaining_And_Usage) return $a; )"); - EXPECT_EQ(valueGlobal, 2); + ASSERT_EQ(valueGlobal.getInt(), 2); - S32 value2 = RunScript(R"( + ConsoleValue value2 = RunScript(R"( function t(%a) { if ((%b = 2 * %a) != 5) @@ -195,26 +155,26 @@ TEST(Script, Variable_Chaining_And_Usage) return t(2); )"); - EXPECT_EQ(value2, 4); + ASSERT_EQ(value2.getInt(), 4); } TEST(Script, Basic_Function_Call_And_Local_Variable_Testing) { - S32 value = RunScript(R"( + ConsoleValue value = RunScript(R"( function t() { %a = 2; return %a; } return t(); )"); - EXPECT_EQ(value, 2); + ASSERT_EQ(value.getInt(), 2); - S32 value2 = RunScript(R"( + ConsoleValue value2 = RunScript(R"( function add(%a, %b) { return %a + %b; } return add(2, 4); )"); - EXPECT_EQ(value2, 6); + ASSERT_EQ(value2.getInt(), 6); - S32 value3 = RunScript(R"( + ConsoleValue value3 = RunScript(R"( function fib(%a) { if (%a == 0) return 0; @@ -225,48 +185,48 @@ TEST(Script, Basic_Function_Call_And_Local_Variable_Testing) return fib(15); )"); - EXPECT_EQ(value3, 610); + ASSERT_EQ(value3.getInt(), 610); - S32 staticCall = RunScript(R"( + ConsoleValue staticCall = RunScript(R"( function SimObject::bar(%a, %b) { return %a + %b; } return SimObject::bar(1, 2); )"); - EXPECT_EQ(staticCall, 3); + ASSERT_EQ(staticCall.getInt(), 3); } TEST(Script, Basic_Conditional_Statements) { - S32 value = RunScript(R"( + ConsoleValue value = RunScript(R"( $a = "hello"; if ($a $= "hello") return 1; return 2; )"); - EXPECT_EQ(value, 1); + ASSERT_EQ(value.getInt(), 1); - const char* ternaryValue = RunScript(R"( + ConsoleValue ternaryValue = RunScript(R"( return $a $= "hello" ? "World" : "No U"; )"); - EXPECT_STREQ(ternaryValue, "World"); + ASSERT_STREQ(ternaryValue.getString(), "World"); } TEST(Script, Basic_Loop_Statements) { - S32 whileValue = RunScript(R"( + ConsoleValue whileValue = RunScript(R"( $count = 0; while ($count < 5) $count++; return $count; )"); - EXPECT_EQ(whileValue, 5); + ASSERT_EQ(whileValue.getInt(), 5); - const char* forValue = RunScript(R"( + ConsoleValue forValue = RunScript(R"( function t(%times) { %result = ""; @@ -278,9 +238,9 @@ TEST(Script, Basic_Loop_Statements) return t(3); )"); - EXPECT_STREQ(forValue, "aaa"); + ASSERT_STREQ(forValue.getString(), "aaa"); - const char* forIfValue = RunScript(R"( + ConsoleValue forIfValue = RunScript(R"( function t() { %str = ""; for (%i = 0; %i < 5; %i++) { @@ -298,43 +258,43 @@ TEST(Script, Basic_Loop_Statements) return t(); )"); - EXPECT_STREQ(forIfValue, "0, 1, 2, 3, 4"); + ASSERT_STREQ(forIfValue.getString(), "0, 1, 2, 3, 4"); } TEST(Script, TorqueScript_Array_Testing) { - S32 value = RunScript(R"( + ConsoleValue value = RunScript(R"( function t(%idx) { %a[idx] = 2; return %a[idx]; } return t(5); )"); - EXPECT_EQ(value, 2); + ASSERT_EQ(value.getInt(), 2); - S32 value2 = RunScript(R"( + ConsoleValue value2 = RunScript(R"( function t(%idx) { %a[idx, 0] = 2; return %a[idx, 0]; } return t(5); )"); - EXPECT_EQ(value2, 2); + ASSERT_EQ(value2.getInt(), 2); } TEST(Script, Basic_SimObject) { - SimObject* object = RunScript(R"( + ConsoleValue object = RunScript(R"( return new SimObject(FudgeCollector) { fudge = "Chocolate"; }; )"); - EXPECT_NE(object, (SimObject*)NULL); + ASSERT_NE(Sim::findObject(object), (SimObject*)NULL); - const char* propertyValue = RunScript(R"( + ConsoleValue propertyValue = RunScript(R"( return FudgeCollector.fudge; )"); - EXPECT_STREQ(propertyValue, "Chocolate"); + ASSERT_STREQ(propertyValue.getString(), "Chocolate"); - const char* funcReturn = RunScript(R"( + ConsoleValue funcReturn = RunScript(R"( function SimObject::fooFunc(%this) { return "Bar"; @@ -343,9 +303,9 @@ TEST(Script, Basic_SimObject) return FudgeCollector.fooFunc(); )"); - EXPECT_STREQ(funcReturn, "Bar"); + ASSERT_STREQ(funcReturn.getString(), "Bar"); - const char* parentFn = RunScript(R"( + ConsoleValue parentFn = RunScript(R"( new SimObject(Hello); function SimObject::fooFunc2(%this) @@ -362,12 +322,12 @@ TEST(Script, Basic_SimObject) return Hello.fooFunc2(); )"); - EXPECT_STREQ(parentFn, "FooBar"); + ASSERT_STREQ(parentFn.getString(), "FooBar"); } TEST(Script, Basic_Package) { - S32 value = RunScript(R"( + ConsoleValue value = RunScript(R"( function a() { return 3; } package overrides { function a() { return 5; } @@ -375,21 +335,21 @@ TEST(Script, Basic_Package) return a(); )"); - EXPECT_EQ(value, 3); + ASSERT_EQ(value.getInt(), 3); - S32 overrideValue = RunScript(R"( + ConsoleValue overrideValue = RunScript(R"( activatePackage(overrides); return a(); )"); - EXPECT_EQ(overrideValue, 5); + ASSERT_EQ(overrideValue.getInt(), 5); - S32 deactivatedValue = RunScript(R"( + ConsoleValue deactivatedValue = RunScript(R"( deactivatePackage(overrides); return a(); )"); - EXPECT_EQ(deactivatedValue, 3); + ASSERT_EQ(deactivatedValue.getInt(), 3); } #endif diff --git a/Engine/source/core/strings/stringFunctions.cpp b/Engine/source/core/strings/stringFunctions.cpp index ed810ea74..893dc5815 100644 --- a/Engine/source/core/strings/stringFunctions.cpp +++ b/Engine/source/core/strings/stringFunctions.cpp @@ -428,7 +428,7 @@ S32 dStrlcpy(char *dst, const char *src, dsize_t dstSize) S32 copyLen = srcLen; //Check for buffer overflow and don't allow it. Warn on debug so we can fix it - AssertWarn(copyLen < dstSize, "Buffer too small in call to dStrlcpy!"); + AssertFatal(copyLen < dstSize, "Buffer too small in call to dStrlcpy!"); if (srcLen + 1 > dstSize) { copyLen = dstSize - 1;